From 6eb14522b65d581e4bbe409883997869833b2860 Mon Sep 17 00:00:00 2001 From: Douglas <32344964+NotTheDr01ds@users.noreply.github.com> Date: Mon, 6 Jan 2025 18:37:51 -0500 Subject: [PATCH] Remove deprecated commands (#14726) # Description Remove commands which were deprecated in 0.101: * `split-by` (#14019) * `date to-record` and `date to-table` (#14319) # User-Facing Changes - :green_circle: `toolkit fmt` - :green_circle: `toolkit clippy` - :green_circle: `toolkit test` - :green_circle: `toolkit test stdlib` # After Submitting TODO: `grep` (`ag`) doc repo for any usage of these commands --- crates/nu-command/src/date/mod.rs | 4 - crates/nu-command/src/date/to_record.rs | 148 ----------- crates/nu-command/src/date/to_table.rs | 146 ----------- crates/nu-command/src/default_context.rs | 3 - crates/nu-command/src/filters/mod.rs | 2 - crates/nu-command/src/filters/split_by.rs | 257 ------------------- crates/nu-command/src/filters/uniq_by.rs | 1 - crates/nu-command/src/sort_utils.rs | 1 - crates/nu-command/tests/commands/mod.rs | 1 - crates/nu-command/tests/commands/split_by.rs | 66 ----- tests/repl/test_parser.rs | 2 +- 11 files changed, 1 insertion(+), 630 deletions(-) delete mode 100644 crates/nu-command/src/date/to_record.rs delete mode 100644 crates/nu-command/src/date/to_table.rs delete mode 100644 crates/nu-command/src/filters/split_by.rs delete mode 100644 crates/nu-command/tests/commands/split_by.rs diff --git a/crates/nu-command/src/date/mod.rs b/crates/nu-command/src/date/mod.rs index dcb3d8bee8..fc0aaf95c0 100644 --- a/crates/nu-command/src/date/mod.rs +++ b/crates/nu-command/src/date/mod.rs @@ -3,8 +3,6 @@ mod humanize; mod list_timezone; mod now; mod parser; -mod to_record; -mod to_table; mod to_timezone; mod utils; @@ -12,7 +10,5 @@ pub use date_::Date; pub use humanize::SubCommand as DateHumanize; pub use list_timezone::SubCommand as DateListTimezones; pub use now::SubCommand as DateNow; -pub use to_record::SubCommand as DateToRecord; -pub use to_table::SubCommand as DateToTable; pub use to_timezone::SubCommand as DateToTimezone; pub(crate) use utils::{generate_strftime_list, parse_date_from_string}; diff --git a/crates/nu-command/src/date/to_record.rs b/crates/nu-command/src/date/to_record.rs deleted file mode 100644 index fc28ba46f5..0000000000 --- a/crates/nu-command/src/date/to_record.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::date::utils::parse_date_from_string; -use chrono::{DateTime, Datelike, FixedOffset, Local, Timelike}; -use nu_engine::command_prelude::*; -use nu_protocol::{report_parse_warning, ParseWarning}; - -#[derive(Clone)] -pub struct SubCommand; - -impl Command for SubCommand { - fn name(&self) -> &str { - "date to-record" - } - - fn signature(&self) -> Signature { - Signature::build("date to-record") - .input_output_types(vec![ - (Type::Date, Type::record()), - (Type::String, Type::record()), - ]) - .allow_variants_without_examples(true) // https://github.com/nushell/nushell/issues/7032 - .category(Category::Deprecated) - } - - fn description(&self) -> &str { - "Convert the date into a record." - } - - fn search_terms(&self) -> Vec<&str> { - vec!["structured", "table"] - } - - fn run( - &self, - engine_state: &EngineState, - _stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - let head = call.head; - report_parse_warning( - &StateWorkingSet::new(engine_state), - &ParseWarning::DeprecatedWarning { - old_command: "date to-record".into(), - new_suggestion: "see `into record` command examples".into(), - span: head, - url: "`help into record`".into(), - }, - ); - - let head = call.head; - // This doesn't match explicit nulls - if matches!(input, PipelineData::Empty) { - return Err(ShellError::PipelineEmpty { dst_span: head }); - } - input.map(move |value| helper(value, head), engine_state.signals()) - } - - fn examples(&self) -> Vec { - vec![ - Example { - description: "Convert the current date into a record.", - example: "date now | date to-record", - result: None, - }, - Example { - description: "Convert a date string into a record.", - example: "'2020-04-12T22:10:57.123+02:00' | date to-record", - result: Some(Value::test_record(record!( - "year" => Value::test_int(2020), - "month" => Value::test_int(4), - "day" => Value::test_int(12), - "hour" => Value::test_int(22), - "minute" => Value::test_int(10), - "second" => Value::test_int(57), - "nanosecond" => Value::test_int(123_000_000), - "timezone" => Value::test_string("+02:00"), - ))), - }, - Example { - description: "Convert a date into a record.", - example: "'2020-04-12 22:10:57 +0200' | into datetime | date to-record", - result: Some(Value::test_record(record!( - "year" => Value::test_int(2020), - "month" => Value::test_int(4), - "day" => Value::test_int(12), - "hour" => Value::test_int(22), - "minute" => Value::test_int(10), - "second" => Value::test_int(57), - "nanosecond" => Value::test_int(0), - "timezone" => Value::test_string("+02:00"), - ))), - }, - ] - } -} - -fn parse_date_into_table(date: DateTime, head: Span) -> Value { - Value::record( - record! { - "year" => Value::int(date.year() as i64, head), - "month" => Value::int(date.month() as i64, head), - "day" => Value::int(date.day() as i64, head), - "hour" => Value::int(date.hour() as i64, head), - "minute" => Value::int(date.minute() as i64, head), - "second" => Value::int(date.second() as i64, head), - "nanosecond" => Value::int(date.nanosecond() as i64, head), - "timezone" => Value::string(date.offset().to_string(), head), - }, - head, - ) -} - -fn helper(val: Value, head: Span) -> Value { - let span = val.span(); - match val { - Value::String { val, .. } => match parse_date_from_string(&val, span) { - Ok(date) => parse_date_into_table(date, head), - Err(e) => e, - }, - Value::Nothing { .. } => { - let now = Local::now(); - let n = now.with_timezone(now.offset()); - parse_date_into_table(n, head) - } - Value::Date { val, .. } => parse_date_into_table(val, head), - _ => Value::error( - ShellError::OnlySupportsThisInputType { - exp_input_type: "date, string (that represents datetime), or nothing".into(), - wrong_type: val.get_type().to_string(), - dst_span: head, - src_span: span, - }, - head, - ), - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_examples() { - use crate::test_examples; - - test_examples(SubCommand {}) - } -} diff --git a/crates/nu-command/src/date/to_table.rs b/crates/nu-command/src/date/to_table.rs deleted file mode 100644 index cb635b039f..0000000000 --- a/crates/nu-command/src/date/to_table.rs +++ /dev/null @@ -1,146 +0,0 @@ -use crate::date::utils::parse_date_from_string; -use chrono::{DateTime, Datelike, FixedOffset, Local, Timelike}; -use nu_engine::command_prelude::*; -use nu_protocol::{report_parse_warning, ParseWarning}; - -#[derive(Clone)] -pub struct SubCommand; - -impl Command for SubCommand { - fn name(&self) -> &str { - "date to-table" - } - - fn signature(&self) -> Signature { - Signature::build("date to-table") - .input_output_types(vec![ - (Type::Date, Type::table()), - (Type::String, Type::table()), - ]) - .allow_variants_without_examples(true) // https://github.com/nushell/nushell/issues/7032 - .category(Category::Deprecated) - } - - fn description(&self) -> &str { - "Convert the date into a structured table." - } - - fn search_terms(&self) -> Vec<&str> { - vec!["structured"] - } - - fn run( - &self, - engine_state: &EngineState, - _stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - let head = call.head; - report_parse_warning( - &StateWorkingSet::new(engine_state), - &ParseWarning::DeprecatedWarning { - old_command: "date to-table".into(), - new_suggestion: "see `into record` command examples".into(), - span: head, - url: "`help into record`".into(), - }, - ); - - // This doesn't match explicit nulls - if matches!(input, PipelineData::Empty) { - return Err(ShellError::PipelineEmpty { dst_span: head }); - } - input.map(move |value| helper(value, head), engine_state.signals()) - } - - fn examples(&self) -> Vec { - vec![ - Example { - description: "Convert the current date into a table.", - example: "date now | date to-table", - result: None, - }, - Example { - description: "Convert a given date into a table.", - example: "2020-04-12T22:10:57.000000789+02:00 | date to-table", - result: Some(Value::test_list(vec![Value::test_record(record!( - "year" => Value::test_int(2020), - "month" => Value::test_int(4), - "day" => Value::test_int(12), - "hour" => Value::test_int(22), - "minute" => Value::test_int(10), - "second" => Value::test_int(57), - "nanosecond" => Value::test_int(789), - "timezone" => Value::test_string("+02:00".to_string()), - ))])), - }, - Example { - description: "Convert a given date into a table.", - example: "'2020-04-12 22:10:57 +0200' | into datetime | date to-table", - result: Some(Value::test_list(vec![Value::test_record(record!( - "year" => Value::test_int(2020), - "month" => Value::test_int(4), - "day" => Value::test_int(12), - "hour" => Value::test_int(22), - "minute" => Value::test_int(10), - "second" => Value::test_int(57), - "nanosecond" => Value::test_int(0), - "timezone" => Value::test_string("+02:00".to_string()), - ))])), - }, - ] - } -} - -fn parse_date_into_table(date: DateTime, head: Span) -> Value { - let record = record! { - "year" => Value::int(date.year() as i64, head), - "month" => Value::int(date.month() as i64, head), - "day" => Value::int(date.day() as i64, head), - "hour" => Value::int(date.hour() as i64, head), - "minute" => Value::int(date.minute() as i64, head), - "second" => Value::int(date.second() as i64, head), - "nanosecond" => Value::int(date.nanosecond() as i64, head), - "timezone" => Value::string(date.offset().to_string(), head), - }; - - Value::list(vec![Value::record(record, head)], head) -} - -fn helper(val: Value, head: Span) -> Value { - let val_span = val.span(); - match val { - Value::String { val, .. } => match parse_date_from_string(&val, val_span) { - Ok(date) => parse_date_into_table(date, head), - Err(e) => e, - }, - Value::Nothing { .. } => { - let now = Local::now(); - let n = now.with_timezone(now.offset()); - parse_date_into_table(n, head) - } - Value::Date { val, .. } => parse_date_into_table(val, head), - _ => Value::error( - ShellError::OnlySupportsThisInputType { - exp_input_type: "date, string (that represents datetime), or nothing".into(), - wrong_type: val.get_type().to_string(), - dst_span: head, - src_span: val_span, - }, - head, - ), - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_examples() { - use crate::test_examples; - - test_examples(SubCommand {}) - } -} diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index d4b08d80c1..4ae348eb14 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -58,7 +58,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { Interleave, Items, Join, - SplitBy, Take, Merge, MergeDeep, @@ -275,8 +274,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { DateHumanize, DateListTimezones, DateNow, - DateToRecord, - DateToTable, DateToTimezone, }; diff --git a/crates/nu-command/src/filters/mod.rs b/crates/nu-command/src/filters/mod.rs index 373314fb18..eaa9452b31 100644 --- a/crates/nu-command/src/filters/mod.rs +++ b/crates/nu-command/src/filters/mod.rs @@ -42,7 +42,6 @@ mod shuffle; mod skip; mod sort; mod sort_by; -mod split_by; mod take; mod tee; mod transpose; @@ -102,7 +101,6 @@ pub use shuffle::Shuffle; pub use skip::*; pub use sort::Sort; pub use sort_by::SortBy; -pub use split_by::SplitBy; pub use take::*; pub use tee::Tee; pub use transpose::Transpose; diff --git a/crates/nu-command/src/filters/split_by.rs b/crates/nu-command/src/filters/split_by.rs deleted file mode 100644 index bbc87452af..0000000000 --- a/crates/nu-command/src/filters/split_by.rs +++ /dev/null @@ -1,257 +0,0 @@ -use indexmap::IndexMap; -use nu_engine::command_prelude::*; -use nu_protocol::report_shell_warning; - -#[derive(Clone)] -pub struct SplitBy; - -impl Command for SplitBy { - fn name(&self) -> &str { - "split-by" - } - - fn signature(&self) -> Signature { - Signature::build("split-by") - .input_output_types(vec![(Type::record(), Type::record())]) - .optional("splitter", SyntaxShape::Any, "The splitter value to use.") - .category(Category::Deprecated) - } - - fn description(&self) -> &str { - "Split a record into groups." - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, - ) -> Result { - report_shell_warning( - engine_state, - &ShellError::Deprecated { - deprecated: "The `split_by` command", - suggestion: "Please use the `group-by` command instead.", - span: call.head, - help: None, - }, - ); - split_by(engine_state, stack, call, input) - } - - fn examples(&self) -> Vec { - vec![Example { - description: "split items by column named \"lang\"", - example: r#"{ - '2019': [ - { name: 'andres', lang: 'rb', year: '2019' }, - { name: 'jt', lang: 'rs', year: '2019' } - ], - '2021': [ - { name: 'storm', lang: 'rs', 'year': '2021' } - ] - } | split-by lang"#, - result: Some(Value::test_record(record! { - "rb" => Value::test_record(record! { - "2019" => Value::test_list( - vec![Value::test_record(record! { - "name" => Value::test_string("andres"), - "lang" => Value::test_string("rb"), - "year" => Value::test_string("2019"), - })], - ), - }), - "rs" => Value::test_record(record! { - "2019" => Value::test_list( - vec![Value::test_record(record! { - "name" => Value::test_string("jt"), - "lang" => Value::test_string("rs"), - "year" => Value::test_string("2019"), - })], - ), - "2021" => Value::test_list( - vec![Value::test_record(record! { - "name" => Value::test_string("storm"), - "lang" => Value::test_string("rs"), - "year" => Value::test_string("2021"), - })], - ), - }), - })), - }] - } -} - -enum Grouper { - ByColumn(Option>), -} - -pub fn split_by( - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, -) -> Result { - let name = call.head; - let config = engine_state.get_config(); - let splitter: Option = call.opt(engine_state, stack, 0)?; - - match splitter { - Some(v) => { - let splitter = Some(Spanned { - item: v.to_abbreviated_string(config), - span: name, - }); - Ok(split(splitter.as_ref(), input, name, config)?) - } - // This uses the same format as the 'requires a column name' error in sort_utils.rs - None => Err(ShellError::GenericError { - error: "expected name".into(), - msg: "requires a column name for splitting".into(), - span: Some(name), - help: None, - inner: vec![], - }), - } -} - -pub fn split( - column_name: Option<&Spanned>, - values: PipelineData, - span: Span, - config: &nu_protocol::Config, -) -> Result { - let grouper = if let Some(column_name) = column_name { - Grouper::ByColumn(Some(column_name.clone())) - } else { - Grouper::ByColumn(None) - }; - - match grouper { - Grouper::ByColumn(Some(column_name)) => { - let block = move |_, row: &Value| { - let group_key = if let Value::Record { val: row, .. } = row { - row.get(&column_name.item) - } else { - None - }; - - match group_key { - Some(group_key) => Ok(group_key.to_abbreviated_string(config)), - None => Err(ShellError::CantFindColumn { - col_name: column_name.item.to_string(), - span: Some(column_name.span), - src_span: row.span(), - }), - } - }; - - data_split(values, Some(&block), span, config) - } - Grouper::ByColumn(None) => { - let block = move |_, row: &Value| Ok(row.to_abbreviated_string(config)); - - data_split(values, Some(&block), span, config) - } - } -} - -#[allow(clippy::type_complexity)] -fn data_group( - values: &Value, - grouper: Option<&dyn Fn(usize, &Value) -> Result>, - span: Span, - config: &nu_protocol::Config, -) -> Result { - let mut groups: IndexMap> = IndexMap::new(); - - for (idx, value) in values.clone().into_pipeline_data().into_iter().enumerate() { - let group_key = if let Some(ref grouper) = grouper { - grouper(idx, &value) - } else { - Ok(value.to_abbreviated_string(config)) - }; - - let group = groups.entry(group_key?).or_default(); - group.push(value); - } - - Ok(Value::record( - groups - .into_iter() - .map(|(k, v)| (k, Value::list(v, span))) - .collect(), - span, - )) -} - -#[allow(clippy::type_complexity)] -pub fn data_split( - value: PipelineData, - splitter: Option<&dyn Fn(usize, &Value) -> Result>, - dst_span: Span, - config: &nu_protocol::Config, -) -> Result { - let mut splits = indexmap::IndexMap::new(); - - match value { - PipelineData::Value(v, _) => { - let span = v.span(); - match v { - Value::Record { val: grouped, .. } => { - for (outer_key, list) in grouped.into_owned() { - match data_group(&list, splitter, span, config) { - Ok(grouped_vals) => { - if let Value::Record { val: sub, .. } = grouped_vals { - for (inner_key, subset) in sub.into_owned() { - let s: &mut IndexMap = - splits.entry(inner_key).or_default(); - - s.insert(outer_key.clone(), subset.clone()); - } - } - } - Err(reason) => return Err(reason), - } - } - } - _ => { - return Err(ShellError::OnlySupportsThisInputType { - exp_input_type: "Record".into(), - wrong_type: v.get_type().to_string(), - dst_span, - src_span: v.span(), - }) - } - } - } - PipelineData::Empty => return Err(ShellError::PipelineEmpty { dst_span }), - _ => { - return Err(ShellError::PipelineMismatch { - exp_input_type: "record".into(), - dst_span, - src_span: value.span().unwrap_or(Span::unknown()), - }) - } - } - - let record = splits - .into_iter() - .map(|(k, rows)| (k, Value::record(rows.into_iter().collect(), dst_span))) - .collect(); - - Ok(PipelineData::Value(Value::record(record, dst_span), None)) -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_examples() { - use crate::test_examples; - - test_examples(SplitBy {}) - } -} diff --git a/crates/nu-command/src/filters/uniq_by.rs b/crates/nu-command/src/filters/uniq_by.rs index c092cfe6b4..8536cc41a5 100644 --- a/crates/nu-command/src/filters/uniq_by.rs +++ b/crates/nu-command/src/filters/uniq_by.rs @@ -110,7 +110,6 @@ fn validate(vec: &[Value], columns: &[String], span: Span) -> Result<(), ShellEr let val_span = v.span(); if let Value::Record { val: record, .. } = &v { if columns.is_empty() { - // This uses the same format as the 'requires a column name' error in split_by.rs return Err(ShellError::GenericError { error: "expected name".into(), msg: "requires a column name to filter table data".into(), diff --git a/crates/nu-command/src/sort_utils.rs b/crates/nu-command/src/sort_utils.rs index 7d70121096..951a93ead9 100644 --- a/crates/nu-command/src/sort_utils.rs +++ b/crates/nu-command/src/sort_utils.rs @@ -58,7 +58,6 @@ pub fn sort_by( natural: bool, ) -> Result<(), ShellError> { if comparators.is_empty() { - // This uses the same format as the 'requires a column name' error in split_by.rs return Err(ShellError::GenericError { error: "expected name".into(), msg: "requires a cell path or closure to sort data".into(), diff --git a/crates/nu-command/tests/commands/mod.rs b/crates/nu-command/tests/commands/mod.rs index eb90d56d1e..c772e78544 100644 --- a/crates/nu-command/tests/commands/mod.rs +++ b/crates/nu-command/tests/commands/mod.rs @@ -103,7 +103,6 @@ mod skip; mod sort; mod sort_by; mod source_env; -mod split_by; mod split_column; mod split_row; mod str_; diff --git a/crates/nu-command/tests/commands/split_by.rs b/crates/nu-command/tests/commands/split_by.rs deleted file mode 100644 index 1f648a1fcf..0000000000 --- a/crates/nu-command/tests/commands/split_by.rs +++ /dev/null @@ -1,66 +0,0 @@ -use nu_test_support::fs::Stub::EmptyFile; -use nu_test_support::playground::Playground; -use nu_test_support::{nu, pipeline}; - -#[test] -fn splits() { - let sample = r#" - [[first_name, last_name, rusty_at, type]; - [Andrés, Robalino, "10/11/2013", A], - [JT, Turner, "10/12/2013", B], - [Yehuda, Katz, "10/11/2013", A]] - "#; - - let actual = nu!(pipeline(&format!( - r#" - {sample} - | group-by rusty_at - | split-by type - | get A."10/11/2013" - | length - "# - ))); - - assert_eq!(actual.out, "2"); -} - -#[test] -fn errors_if_no_input() { - Playground::setup("split_by_no_input", |dirs, _sandbox| { - let actual = nu!(cwd: dirs.test(), pipeline("split-by type")); - - assert!(actual.err.contains("no input value was piped in")); - }) -} - -#[test] -fn errors_if_non_record_input() { - Playground::setup("split_by_test_2", |dirs, sandbox| { - sandbox.with_files(&[ - EmptyFile("los.txt"), - EmptyFile("tres.txt"), - EmptyFile("amigos.txt"), - EmptyFile("arepas.clu"), - ]); - - let input_mismatch = nu!(cwd: dirs.test(), pipeline("5 | split-by type")); - - assert!(input_mismatch.err.contains("doesn't support int input")); - - let only_supports = nu!( - cwd: dirs.test(), pipeline( - " - ls - | get name - | split-by type - " - )); - - assert!( - only_supports - .err - .contains("only Record input data is supported") - || only_supports.err.contains("expected: record") - ); - }) -} diff --git a/tests/repl/test_parser.rs b/tests/repl/test_parser.rs index ddc790324a..988b34f18d 100644 --- a/tests/repl/test_parser.rs +++ b/tests/repl/test_parser.rs @@ -663,7 +663,7 @@ fn comment_in_multiple_pipelines() -> TestResult { #[test] fn date_literal() -> TestResult { - run_test(r#"2022-09-10 | date to-record | get day"#, "10") + run_test(r#"2022-09-10 | into record | get day"#, "10") } #[test]