diff --git a/README.md b/README.md index 75bfe7d56..5baa3e5ce 100644 --- a/README.md +++ b/README.md @@ -270,6 +270,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | append row-data | Append a row to the end of the table | | compact ...columns | Remove rows where given columns are empty | | count | Show the total number of rows | +| default column row-data | Sets a default row's column if missing | | edit column-or-column-path value | Edit an existing column to have a new value | | embed column | Creates a new table of one column with the given name, and places the current table inside of it | | first amount | Show only the first number of rows | diff --git a/src/cli.rs b/src/cli.rs index 0354b642e..64d562f97 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -310,6 +310,7 @@ pub async fn cli() -> Result<(), Box> { per_item_command(Echo), whole_stream_command(Config), whole_stream_command(Compact), + whole_stream_command(Default), whole_stream_command(SkipWhile), per_item_command(Enter), per_item_command(Help), diff --git a/src/commands.rs b/src/commands.rs index 0d8c0c898..705deb1d5 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -17,6 +17,7 @@ pub(crate) mod count; pub(crate) mod cp; pub(crate) mod date; pub(crate) mod debug; +pub(crate) mod default; pub(crate) mod echo; pub(crate) mod enter; pub(crate) mod env; @@ -105,6 +106,7 @@ pub(crate) use count::Count; pub(crate) use cp::Cpy; pub(crate) use date::Date; pub(crate) use debug::Debug; +pub(crate) use default::Default; pub(crate) use echo::Echo; pub(crate) use enter::Enter; pub(crate) use env::Env; diff --git a/src/commands/compact.rs b/src/commands/compact.rs index be18b8ce4..4660e88ad 100644 --- a/src/commands/compact.rs +++ b/src/commands/compact.rs @@ -37,13 +37,19 @@ pub fn compact( CompactArgs { rest: columns }: CompactArgs, RunnableContext { input, .. }: RunnableContext, ) -> Result { - let objects = input.values.take_while(move |item| { + let objects = input.values.filter(move |item| { let keep = if columns.is_empty() { item.is_some() } else { - columns - .iter() - .all(|field| item.get_data(field).borrow().is_some()) + match item { + Tagged { + item: Value::Row(ref r), + .. + } => columns + .iter() + .all(|field| r.get_data(field).borrow().is_some()), + _ => false, + } }; futures::future::ready(keep) diff --git a/src/commands/default.rs b/src/commands/default.rs new file mode 100644 index 000000000..52d33e463 --- /dev/null +++ b/src/commands/default.rs @@ -0,0 +1,72 @@ +use crate::commands::WholeStreamCommand; +use crate::errors::ShellError; +use crate::parser::CommandRegistry; +use crate::prelude::*; + +#[derive(Deserialize)] +struct DefaultArgs { + column: Tagged, + value: Tagged, +} + +pub struct Default; + +impl WholeStreamCommand for Default { + fn name(&self) -> &str { + "default" + } + + fn signature(&self) -> Signature { + Signature::build("default") + .required("column name", SyntaxShape::String, "the name of the column") + .required( + "column value", + SyntaxShape::Any, + "the value of the column to default", + ) + } + + fn usage(&self) -> &str { + "Sets a default row's column if missing." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, default)?.run() + } +} + +fn default( + DefaultArgs { column, value }: DefaultArgs, + RunnableContext { input, .. }: RunnableContext, +) -> Result { + let stream = input + .values + .map(move |item| { + let mut result = VecDeque::new(); + + let should_add = match item { + Tagged { + item: Value::Row(ref r), + .. + } => r.get_data(&column.item).borrow().is_none(), + _ => false, + }; + + if should_add { + match item.insert_data_at_path(&column.item, value.item.clone()) { + Some(new_value) => result.push_back(ReturnSuccess::value(new_value)), + None => result.push_back(ReturnSuccess::value(item)), + } + } else { + result.push_back(ReturnSuccess::value(item)); + } + result + }) + .flatten(); + + Ok(stream.to_output_stream()) +} diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 646cf57cb..59621539d 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -3,6 +3,38 @@ mod helpers; use helpers as h; use helpers::{Playground, Stub::*}; +#[test] +fn default_row_data_if_column_missing() { + Playground::setup("default_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_amigos.json", + r#" + { + "amigos": [ + {"name": "Yehuda"}, + {"name": "Jonathan", "rusty_luck": 0}, + {"name": "Andres", "rusty_luck": 0}, + {"name":"GorbyPuff"} + ] + } + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + open los_tres_amigos.json + | get amigos + | default rusty_luck 1 + | get rusty_luck + | sum + | echo $it + "# + )); + + assert_eq!(actual, "2"); + }); +} #[test] fn compact_rows_where_given_column_is_empty() { Playground::setup("compact_test_1", |dirs, sandbox| {