diff --git a/Cargo.toml b/Cargo.toml index 3b0ce58b46..6929c1e190 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -147,10 +147,6 @@ path = "src/plugins/sum.rs" name = "nu_plugin_average" path = "src/plugins/average.rs" -[[bin]] -name = "nu_plugin_embed" -path = "src/plugins/embed.rs" - [[bin]] name = "nu_plugin_str" path = "src/plugins/str.rs" @@ -168,11 +164,6 @@ name = "nu_plugin_tree" path = "src/plugins/tree.rs" required-features = ["tree"] -[[bin]] -name = "nu_plugin_docker" -path = "src/plugins/docker.rs" -required-features = ["docker"] - [[bin]] name = "nu" path = "src/main.rs" diff --git a/src/cli.rs b/src/cli.rs index 951ea414cc..cbbde8c1bc 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -310,6 +310,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Which), whole_stream_command(Debug), whole_stream_command(Range), + whole_stream_command(Wrap), ]); cfg_if::cfg_if! { diff --git a/src/commands.rs b/src/commands.rs index f8544598e4..4c19bb4675 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -93,6 +93,7 @@ pub(crate) mod version; pub(crate) mod what; pub(crate) mod where_; pub(crate) mod which_; +pub(crate) mod wrap; pub(crate) use autoview::Autoview; pub(crate) use cd::CD; @@ -186,3 +187,4 @@ pub(crate) use version::Version; pub(crate) use what::What; pub(crate) use where_::Where; pub(crate) use which_::Which; +pub(crate) use wrap::Wrap; diff --git a/src/commands/wrap.rs b/src/commands/wrap.rs new file mode 100644 index 0000000000..652b96cebd --- /dev/null +++ b/src/commands/wrap.rs @@ -0,0 +1,102 @@ +use crate::commands::WholeStreamCommand; +use crate::context::CommandRegistry; +use crate::prelude::*; +use indexmap::IndexMap; +use nu_errors::ShellError; +use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_source::Tagged; + +pub struct Wrap; + +#[derive(Deserialize)] +struct WrapArgs { + column: Option>, +} + +impl WholeStreamCommand for Wrap { + fn name(&self) -> &str { + "wrap" + } + + fn signature(&self) -> Signature { + Signature::build("wrap").optional( + "column", + SyntaxShape::String, + "the name of the new column", + ) + } + + fn usage(&self) -> &str { + "Wraps the given data in a table." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, wrap)?.run() + } +} + +fn wrap( + WrapArgs { column }: WrapArgs, + context: RunnableContext, +) -> Result { + let mut input = context.input; + + let stream = async_stream! { + let mut result_table = vec![]; + let mut are_all_rows = true; + + while let Some(value) = input.next().await { + match value { + Value { + value: UntaggedValue::Row(_), + .. + } => { + result_table.push(value); + } + _ => { + are_all_rows = false; + + let mut index_map = IndexMap::new(); + index_map.insert( + match &column { + Some(key) => key.item.clone(), + None => "Column".into(), + }, + value, + ); + + result_table.push(UntaggedValue::row(index_map).into_value(Tag::unknown())); + } + + } + } + + if are_all_rows { + let mut index_map = IndexMap::new(); + index_map.insert( + match &column { + Some(key) => key.item.clone(), + None => "Column".into(), + }, + UntaggedValue::table(&result_table).into_value(Tag::unknown()), + ); + + let row = UntaggedValue::row(index_map).into_untagged_value(); + + yield ReturnSuccess::value(row); + } else { + for item in result_table + .iter() + .map(|row| ReturnSuccess::value(row.clone())) { + + yield item; + } + } + }; + + Ok(stream.to_output_stream()) +} diff --git a/src/plugins/docker.rs b/src/plugins/docker.rs deleted file mode 100644 index a2a80d6c11..0000000000 --- a/src/plugins/docker.rs +++ /dev/null @@ -1,115 +0,0 @@ -use futures::executor::block_on; -use nu::{ - serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxShape, Tag, Tagged, TaggedDictBuilder, Value, -}; - -use std::process::Command; -use std::str; - -struct Docker; - -impl Docker { - fn new() -> Self { - Self - } -} - -async fn docker(sub_command: &String, name: Tag) -> Result, ShellError> { - match sub_command.as_str() { - "ps" => docker_ps(name), - "images" => docker_images(name), - _ => Err(ShellError::labeled_error( - "Unsupported Docker command", - "unknown docker command", - name, - )), - } -} - -fn process_docker_output(cmd_output: &str, tag: Tag) -> Result, ShellError> { - let columns: Vec<&str> = cmd_output.lines().collect(); - - let header: Vec<&str> = columns - .iter() - .take(1) - .next() - .unwrap() - .split_whitespace() - .collect(); - - let mut output = vec![]; - for line in columns.iter().skip(1) { - let values: Vec<&str> = line - .trim_end() - .split(" ") // Some columns values contains spaces to split by two spaces - .filter(|s| s.trim() != "") - .collect(); - - let mut dict = TaggedDictBuilder::new(&tag); - for (i, v) in values.iter().enumerate() { - dict.insert(header[i].to_string(), value::string(v.trim().to_string())); - } - - output.push(dict.into_value()); - } - - Ok(output) -} - -pub fn docker_images(tag: Tag) -> Result, ShellError> { - let output = Command::new("docker") - .arg("images") - .output() - .expect("failed to execute process."); - - let ps_output = str::from_utf8(&output.stdout).unwrap(); - let out = process_docker_output(ps_output, tag); - - out -} - -pub fn docker_ps(tag: Tag) -> Result, ShellError> { - let output = Command::new("docker") - .arg("ps") - .output() - .expect("failed to execute process."); - - let ps_output = str::from_utf8(&output.stdout).unwrap(); - let out = process_docker_output(ps_output, tag); - - out -} - -impl Plugin for Docker { - fn config(&mut self) -> Result { - Ok(Signature::build("docker") - .required("sub_command", SyntaxShape::Member) - .filter()) - } - - fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { - if let Some(args) = callinfo.args.positional { - match &args[0] { - Value { - value: UntaggedValue::Primitive(Primitive::String(command)), - .. - } => match block_on(docker(&command, args[0].tag())) { - Ok(v) => return Ok(v.into_iter().map(ReturnSuccess::value).collect()), - Err(e) => return Err(e), - }, - _ => return Err(ShellError::type_error("string", args[0].tagged_type_name())), - } - } - - Ok(vec![]) - } - - fn filter(&mut self, _: Value) -> Result, ShellError> { - Ok(vec![]) - } -} - -fn main() { - serve_plugin(&mut Docker::new()); -} diff --git a/src/plugins/embed.rs b/src/plugins/embed.rs deleted file mode 100644 index 2c53385bec..0000000000 --- a/src/plugins/embed.rs +++ /dev/null @@ -1,105 +0,0 @@ -#[macro_use] -extern crate indexmap; - -use nu::{serve_plugin, Plugin}; -use nu_errors::ShellError; -use nu_protocol::{ - CallInfo, Primitive, ReturnSuccess, ReturnValue, Signature, SpannedTypeName, SyntaxShape, - UntaggedValue, Value, -}; -use nu_source::Tag; - -struct Embed { - field: Option, - are_all_rows: bool, - values: Vec, -} -impl Embed { - fn new() -> Embed { - Embed { - field: None, - are_all_rows: true, - values: Vec::new(), - } - } - - fn embed(&mut self, value: Value) -> Result<(), ShellError> { - match &value { - Value { - value: UntaggedValue::Row(_), - .. - } => { - self.values.push(value); - } - _ => { - self.are_all_rows = false; - - self.values.push( - UntaggedValue::row(indexmap! { - match &self.field { - Some(key) => key.clone(), - None => "Column".into() - } => value - }) - .into_value(Tag::unknown()), - ); - } - } - Ok(()) - } -} - -impl Plugin for Embed { - fn config(&mut self) -> Result { - Ok(Signature::build("embed") - .desc("Embeds a new field to the table.") - .optional("field", SyntaxShape::String, "the name of the new column") - .filter()) - } - - fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { - if let Some(args) = call_info.args.positional { - match &args[0] { - Value { - value: UntaggedValue::Primitive(Primitive::String(s)), - .. - } => { - self.field = Some(s.clone()); - self.values = Vec::new(); - } - value => return Err(ShellError::type_error("string", value.spanned_type_name())), - } - } - - Ok(vec![]) - } - - fn filter(&mut self, input: Value) -> Result, ShellError> { - self.embed(input)?; - Ok(vec![]) - } - - fn end_filter(&mut self) -> Result, ShellError> { - if self.are_all_rows { - let row = UntaggedValue::row(indexmap! { - match &self.field { - Some(key) => key.clone(), - None => "Column".into(), - } => UntaggedValue::table(&self.values).into_value(Tag::unknown()), - }) - .into_untagged_value(); - - Ok(vec![ReturnSuccess::value(row)]) - } else { - Ok(self - .values - .iter() - .map(|row| ReturnSuccess::value(row.clone())) - .collect::>()) - } - } -} - -fn main() { - serve_plugin(&mut Embed::new()); -} diff --git a/tests/filters_test.rs b/tests/filters_test.rs index f4ee1c70f6..889e588cea 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -729,7 +729,7 @@ fn can_get_reverse_first() { } #[test] -fn embed_rows_into_a_row() { +fn wrap_rows_into_a_row() { Playground::setup("embed_test_1", |dirs, sandbox| { sandbox.with_files(vec![FileWithContentToBeTrimmed( "los_tres_caballeros.txt", @@ -746,7 +746,7 @@ fn embed_rows_into_a_row() { r#" open los_tres_caballeros.txt | from-csv - | embed caballeros + | wrap caballeros | get caballeros | nth 0 | get last_name @@ -759,7 +759,7 @@ fn embed_rows_into_a_row() { } #[test] -fn embed_rows_into_a_table() { +fn wrap_rows_into_a_table() { Playground::setup("embed_test_2", |dirs, sandbox| { sandbox.with_files(vec![FileWithContentToBeTrimmed( "los_tres_caballeros.txt", @@ -777,7 +777,7 @@ fn embed_rows_into_a_table() { open los_tres_caballeros.txt | from-csv | get last_name - | embed caballero + | wrap caballero | nth 2 | get caballero | echo $it