From 786ba3bf91f94c7c9f02762847c04aec54dacae5 Mon Sep 17 00:00:00 2001 From: JT <547158+jntrnr@users.noreply.github.com> Date: Fri, 14 Jul 2023 15:20:35 +1200 Subject: [PATCH] Input output checking (#9680) # Description This PR tights input/output type-checking a bit more. There are a lot of commands that don't have correct input/output types, so part of the effort is updating them. This PR now contains updates to commands that had wrong input/output signatures. It doesn't add examples for these new signatures, but that can be follow-up work. # User-Facing Changes BREAKING CHANGE BREAKING CHANGE This work enforces many more checks on pipeline type correctness than previous nushell versions. This strictness may uncover incompatibilities in existing scripts or shortcomings in the type information for internal commands. # Tests + Formatting # After Submitting --- crates/nu-cmd-extra/src/extra/bits/and.rs | 8 +- crates/nu-cmd-extra/src/extra/bits/not.rs | 8 +- crates/nu-cmd-extra/src/extra/bits/or.rs | 8 +- .../src/extra/bits/rotate_left.rs | 8 +- .../src/extra/bits/rotate_right.rs | 8 +- .../nu-cmd-extra/src/extra/bits/shift_left.rs | 8 +- .../src/extra/bits/shift_right.rs | 8 +- crates/nu-cmd-extra/src/extra/bits/xor.rs | 8 +- crates/nu-cmd-extra/src/extra/bytes/length.rs | 8 +- crates/nu-cmd-extra/src/extra/bytes/remove.rs | 5 +- .../nu-cmd-extra/src/extra/bytes/replace.rs | 5 +- crates/nu-cmd-extra/src/extra/math/cos.rs | 8 +- crates/nu-cmd-extra/src/extra/math/sin.rs | 8 +- crates/nu-cmd-extra/src/extra/math/tan.rs | 8 +- .../tests/commands/bytes/starts_with.rs | 2 +- .../nu-cmd-lang/src/core_commands/collect.rs | 2 +- .../src/conversions/into/decimal.rs | 6 + crates/nu-command/src/conversions/into/int.rs | 7 + .../nu-command/src/conversions/into/string.rs | 4 + crates/nu-command/src/env/load_env.rs | 5 +- crates/nu-command/src/filters/append.rs | 12 +- crates/nu-command/src/filters/each.rs | 2 + crates/nu-command/src/filters/find.rs | 2 +- crates/nu-command/src/filters/first.rs | 2 + crates/nu-command/src/filters/get.rs | 2 + crates/nu-command/src/filters/insert.rs | 5 + crates/nu-command/src/filters/select.rs | 2 + crates/nu-command/src/filters/sort_by.rs | 9 +- crates/nu-command/src/filters/transpose.rs | 2 + crates/nu-command/src/filters/uniq_by.rs | 9 +- crates/nu-command/src/filters/update.rs | 5 + crates/nu-command/src/filters/upsert.rs | 5 + crates/nu-command/src/filters/where_.rs | 2 + crates/nu-command/src/filters/wrap.rs | 2 + crates/nu-command/src/math/abs.rs | 9 +- crates/nu-command/src/math/avg.rs | 7 +- crates/nu-command/src/math/ceil.rs | 9 +- crates/nu-command/src/math/floor.rs | 9 +- crates/nu-command/src/math/log.rs | 10 +- crates/nu-command/src/math/max.rs | 1 + crates/nu-command/src/math/median.rs | 1 + crates/nu-command/src/math/min.rs | 1 + crates/nu-command/src/math/mode.rs | 1 + crates/nu-command/src/math/round.rs | 8 +- crates/nu-command/src/math/sqrt.rs | 9 +- crates/nu-command/src/math/sum.rs | 7 +- crates/nu-command/src/network/url/encode.rs | 2 +- crates/nu-command/src/path/join.rs | 2 + crates/nu-command/src/platform/ansi/strip.rs | 3 +- crates/nu-command/src/strings/parse.rs | 6 +- crates/nu-command/src/strings/split/row.rs | 6 +- .../src/strings/str_/case/camel_case.rs | 5 +- .../src/strings/str_/case/capitalize.rs | 5 +- .../src/strings/str_/case/downcase.rs | 5 +- .../src/strings/str_/case/kebab_case.rs | 5 +- .../src/strings/str_/case/pascal_case.rs | 5 +- .../strings/str_/case/screaming_snake_case.rs | 5 +- .../src/strings/str_/case/snake_case.rs | 5 +- .../src/strings/str_/case/title_case.rs | 5 +- .../src/strings/str_/case/upcase.rs | 6 +- .../nu-command/src/strings/str_/contains.rs | 1 + crates/nu-command/src/strings/str_/join.rs | 6 +- crates/nu-command/src/strings/str_/length.rs | 2 +- crates/nu-command/src/strings/str_/replace.rs | 5 +- crates/nu-command/src/strings/str_/reverse.rs | 8 +- .../nu-command/src/strings/str_/substring.rs | 3 +- .../nu-command/src/strings/str_/trim/trim_.rs | 10 +- crates/nu-command/tests/commands/drop.rs | 2 +- crates/nu-command/tests/commands/last.rs | 2 +- .../nu-command/tests/commands/math/round.rs | 2 +- crates/nu-command/tests/commands/mkdir.rs | 1 - crates/nu-command/tests/commands/rename.rs | 2 +- crates/nu-command/tests/commands/reverse.rs | 2 +- .../nu-command/tests/commands/skip/skip_.rs | 2 +- .../nu-command/tests/commands/skip/until.rs | 2 +- .../nu-command/tests/commands/skip/while_.rs | 2 +- crates/nu-command/tests/commands/sort_by.rs | 2 +- crates/nu-command/tests/commands/take/rows.rs | 2 +- .../nu-command/tests/commands/take/until.rs | 2 +- .../nu-command/tests/commands/take/while_.rs | 2 +- crates/nu-command/tests/commands/where_.rs | 2 +- .../tests/format_conversions/csv.rs | 2 +- .../tests/format_conversions/toml.rs | 4 +- crates/nu-parser/src/parser.rs | 4 +- crates/nu-parser/src/type_check.rs | 135 +++++++++++++++++- crates/nu-protocol/src/ast/block.rs | 35 +---- crates/nu-protocol/src/parse_error.rs | 10 ++ src/tests/test_bits.rs | 2 +- src/tests/test_engine.rs | 2 +- 89 files changed, 480 insertions(+), 106 deletions(-) diff --git a/crates/nu-cmd-extra/src/extra/bits/and.rs b/crates/nu-cmd-extra/src/extra/bits/and.rs index e63b6f23e6f8..104cbe7de654 100644 --- a/crates/nu-cmd-extra/src/extra/bits/and.rs +++ b/crates/nu-cmd-extra/src/extra/bits/and.rs @@ -15,7 +15,13 @@ impl Command for BitsAnd { fn signature(&self) -> Signature { Signature::build("bits and") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required( "target", diff --git a/crates/nu-cmd-extra/src/extra/bits/not.rs b/crates/nu-cmd-extra/src/extra/bits/not.rs index 4948a1c4a10a..3e4e72529c1a 100644 --- a/crates/nu-cmd-extra/src/extra/bits/not.rs +++ b/crates/nu-cmd-extra/src/extra/bits/not.rs @@ -16,7 +16,13 @@ impl Command for BitsNot { fn signature(&self) -> Signature { Signature::build("bits not") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .switch( "signed", diff --git a/crates/nu-cmd-extra/src/extra/bits/or.rs b/crates/nu-cmd-extra/src/extra/bits/or.rs index 1c3c7243de38..41110e17cd06 100644 --- a/crates/nu-cmd-extra/src/extra/bits/or.rs +++ b/crates/nu-cmd-extra/src/extra/bits/or.rs @@ -15,7 +15,13 @@ impl Command for BitsOr { fn signature(&self) -> Signature { Signature::build("bits or") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required( "target", diff --git a/crates/nu-cmd-extra/src/extra/bits/rotate_left.rs b/crates/nu-cmd-extra/src/extra/bits/rotate_left.rs index d299dd6dcc11..363c0104280f 100644 --- a/crates/nu-cmd-extra/src/extra/bits/rotate_left.rs +++ b/crates/nu-cmd-extra/src/extra/bits/rotate_left.rs @@ -18,7 +18,13 @@ impl Command for BitsRol { fn signature(&self) -> Signature { Signature::build("bits rol") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required("bits", SyntaxShape::Int, "number of bits to rotate left") .switch( diff --git a/crates/nu-cmd-extra/src/extra/bits/rotate_right.rs b/crates/nu-cmd-extra/src/extra/bits/rotate_right.rs index 9e521a30e79b..c9f4f9ce9f19 100644 --- a/crates/nu-cmd-extra/src/extra/bits/rotate_right.rs +++ b/crates/nu-cmd-extra/src/extra/bits/rotate_right.rs @@ -18,7 +18,13 @@ impl Command for BitsRor { fn signature(&self) -> Signature { Signature::build("bits ror") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required("bits", SyntaxShape::Int, "number of bits to rotate right") .switch( diff --git a/crates/nu-cmd-extra/src/extra/bits/shift_left.rs b/crates/nu-cmd-extra/src/extra/bits/shift_left.rs index 802b29a04f05..05289b282fac 100644 --- a/crates/nu-cmd-extra/src/extra/bits/shift_left.rs +++ b/crates/nu-cmd-extra/src/extra/bits/shift_left.rs @@ -18,7 +18,13 @@ impl Command for BitsShl { fn signature(&self) -> Signature { Signature::build("bits shl") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required("bits", SyntaxShape::Int, "number of bits to shift left") .switch( diff --git a/crates/nu-cmd-extra/src/extra/bits/shift_right.rs b/crates/nu-cmd-extra/src/extra/bits/shift_right.rs index f780ee667297..f94d81829dbc 100644 --- a/crates/nu-cmd-extra/src/extra/bits/shift_right.rs +++ b/crates/nu-cmd-extra/src/extra/bits/shift_right.rs @@ -18,7 +18,13 @@ impl Command for BitsShr { fn signature(&self) -> Signature { Signature::build("bits shr") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required("bits", SyntaxShape::Int, "number of bits to shift right") .switch( diff --git a/crates/nu-cmd-extra/src/extra/bits/xor.rs b/crates/nu-cmd-extra/src/extra/bits/xor.rs index 691ea6907937..c40d5ea587a4 100644 --- a/crates/nu-cmd-extra/src/extra/bits/xor.rs +++ b/crates/nu-cmd-extra/src/extra/bits/xor.rs @@ -15,7 +15,13 @@ impl Command for BitsXor { fn signature(&self) -> Signature { Signature::build("bits xor") - .input_output_types(vec![(Type::Int, Type::Int)]) + .input_output_types(vec![ + (Type::Int, Type::Int), + ( + Type::List(Box::new(Type::Int)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .required( "target", diff --git a/crates/nu-cmd-extra/src/extra/bytes/length.rs b/crates/nu-cmd-extra/src/extra/bytes/length.rs index bab8550a53f2..3f8caaf7215f 100644 --- a/crates/nu-cmd-extra/src/extra/bytes/length.rs +++ b/crates/nu-cmd-extra/src/extra/bytes/length.rs @@ -16,7 +16,13 @@ impl Command for BytesLen { fn signature(&self) -> Signature { Signature::build("bytes length") - .input_output_types(vec![(Type::Binary, Type::Int)]) + .input_output_types(vec![ + (Type::Binary, Type::Int), + ( + Type::List(Box::new(Type::Binary)), + Type::List(Box::new(Type::Int)), + ), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-cmd-extra/src/extra/bytes/remove.rs b/crates/nu-cmd-extra/src/extra/bytes/remove.rs index 8780d14ece25..8db1af62b5af 100644 --- a/crates/nu-cmd-extra/src/extra/bytes/remove.rs +++ b/crates/nu-cmd-extra/src/extra/bytes/remove.rs @@ -30,7 +30,10 @@ impl Command for BytesRemove { fn signature(&self) -> Signature { Signature::build("bytes remove") - .input_output_types(vec![(Type::Binary, Type::Binary)]) + .input_output_types(vec![ + (Type::Binary, Type::Binary), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .required("pattern", SyntaxShape::Binary, "the pattern to find") .rest( "rest", diff --git a/crates/nu-cmd-extra/src/extra/bytes/replace.rs b/crates/nu-cmd-extra/src/extra/bytes/replace.rs index dc12d7e344b8..2370d13eb1e1 100644 --- a/crates/nu-cmd-extra/src/extra/bytes/replace.rs +++ b/crates/nu-cmd-extra/src/extra/bytes/replace.rs @@ -30,7 +30,10 @@ impl Command for BytesReplace { fn signature(&self) -> Signature { Signature::build("bytes replace") - .input_output_types(vec![(Type::Binary, Type::Binary)]) + .input_output_types(vec![ + (Type::Binary, Type::Binary), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .required("find", SyntaxShape::Binary, "the pattern to find") .required("replace", SyntaxShape::Binary, "the replacement pattern") .rest( diff --git a/crates/nu-cmd-extra/src/extra/math/cos.rs b/crates/nu-cmd-extra/src/extra/math/cos.rs index d2b94b7fd9ef..96486431c8e0 100644 --- a/crates/nu-cmd-extra/src/extra/math/cos.rs +++ b/crates/nu-cmd-extra/src/extra/math/cos.rs @@ -13,7 +13,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math cos") .switch("degrees", "Use degrees instead of radians", Some('d')) - .input_output_types(vec![(Type::Number, Type::Float)]) + .input_output_types(vec![ + (Type::Number, Type::Float), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Float)), + ), + ]) .vectorizes_over_list(true) .category(Category::Math) } diff --git a/crates/nu-cmd-extra/src/extra/math/sin.rs b/crates/nu-cmd-extra/src/extra/math/sin.rs index 0db2dbc6da1f..276163eb0641 100644 --- a/crates/nu-cmd-extra/src/extra/math/sin.rs +++ b/crates/nu-cmd-extra/src/extra/math/sin.rs @@ -13,7 +13,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math sin") .switch("degrees", "Use degrees instead of radians", Some('d')) - .input_output_types(vec![(Type::Number, Type::Float)]) + .input_output_types(vec![ + (Type::Number, Type::Float), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Float)), + ), + ]) .vectorizes_over_list(true) .category(Category::Math) } diff --git a/crates/nu-cmd-extra/src/extra/math/tan.rs b/crates/nu-cmd-extra/src/extra/math/tan.rs index a6b3a8e64c86..65b8b61388b4 100644 --- a/crates/nu-cmd-extra/src/extra/math/tan.rs +++ b/crates/nu-cmd-extra/src/extra/math/tan.rs @@ -13,7 +13,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math tan") .switch("degrees", "Use degrees instead of radians", Some('d')) - .input_output_types(vec![(Type::Number, Type::Float)]) + .input_output_types(vec![ + (Type::Number, Type::Float), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Float)), + ), + ]) .vectorizes_over_list(true) .category(Category::Math) } diff --git a/crates/nu-cmd-extra/tests/commands/bytes/starts_with.rs b/crates/nu-cmd-extra/tests/commands/bytes/starts_with.rs index 797d5766e70a..1448c0decd78 100644 --- a/crates/nu-cmd-extra/tests/commands/bytes/starts_with.rs +++ b/crates/nu-cmd-extra/tests/commands/bytes/starts_with.rs @@ -21,7 +21,7 @@ fn basic_string_fails() { "# ); - assert!(actual.err.contains("Input type not supported")); + assert!(actual.err.contains("command doesn't support")); assert_eq!(actual.out, ""); } diff --git a/crates/nu-cmd-lang/src/core_commands/collect.rs b/crates/nu-cmd-lang/src/core_commands/collect.rs index ee827512e43e..f8415e841acf 100644 --- a/crates/nu-cmd-lang/src/core_commands/collect.rs +++ b/crates/nu-cmd-lang/src/core_commands/collect.rs @@ -16,7 +16,7 @@ impl Command for Collect { fn signature(&self) -> Signature { Signature::build("collect") - .input_output_types(vec![(Type::List(Box::new(Type::Any)), Type::Any)]) + .input_output_types(vec![(Type::Any, Type::Any)]) .required( "closure", SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), diff --git a/crates/nu-command/src/conversions/into/decimal.rs b/crates/nu-command/src/conversions/into/decimal.rs index 11ceb0a902c8..66b03bd2f997 100644 --- a/crates/nu-command/src/conversions/into/decimal.rs +++ b/crates/nu-command/src/conversions/into/decimal.rs @@ -17,15 +17,21 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("into decimal") .input_output_types(vec![ + (Type::Int, Type::Number), (Type::String, Type::Number), (Type::Bool, Type::Number), (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Number)), + ), ]) .rest( "rest", SyntaxShape::CellPath, "for a data structure input, convert data at the given cell paths", ) + .allow_variants_without_examples(true) .category(Category::Conversions) } diff --git a/crates/nu-command/src/conversions/into/int.rs b/crates/nu-command/src/conversions/into/int.rs index 7e5c8e73886d..0d3e9dbdce1e 100644 --- a/crates/nu-command/src/conversions/into/int.rs +++ b/crates/nu-command/src/conversions/into/int.rs @@ -36,10 +36,17 @@ impl Command for SubCommand { (Type::Bool, Type::Int), // Unix timestamp in nanoseconds (Type::Date, Type::Int), + (Type::Duration, Type::Int), // TODO: Users should do this by dividing a Filesize by a Filesize explicitly (Type::Filesize, Type::Int), + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Int)), + ), ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .named("radix", SyntaxShape::Number, "radix of integer", Some('r')) .switch("little-endian", "use little-endian byte decoding", None) .rest( diff --git a/crates/nu-command/src/conversions/into/string.rs b/crates/nu-command/src/conversions/into/string.rs index 8ede3f300fa1..699c74f8c851 100644 --- a/crates/nu-command/src/conversions/into/string.rs +++ b/crates/nu-command/src/conversions/into/string.rs @@ -40,6 +40,10 @@ impl Command for SubCommand { (Type::Bool, Type::String), (Type::Filesize, Type::String), (Type::Date, Type::String), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::String)), + ), ]) .allow_variants_without_examples(true) // https://github.com/nushell/nushell/issues/7032 .rest( diff --git a/crates/nu-command/src/env/load_env.rs b/crates/nu-command/src/env/load_env.rs index 0a84fa100d67..9f1102903acf 100644 --- a/crates/nu-command/src/env/load_env.rs +++ b/crates/nu-command/src/env/load_env.rs @@ -19,7 +19,10 @@ impl Command for LoadEnv { fn signature(&self) -> nu_protocol::Signature { Signature::build("load-env") - .input_output_types(vec![(Type::Record(vec![]), Type::Nothing)]) + .input_output_types(vec![ + (Type::Record(vec![]), Type::Nothing), + (Type::Nothing, Type::Nothing), + ]) .allow_variants_without_examples(true) .optional( "update", diff --git a/crates/nu-command/src/filters/append.rs b/crates/nu-command/src/filters/append.rs index d7b529e300e9..d5af53b7d128 100644 --- a/crates/nu-command/src/filters/append.rs +++ b/crates/nu-command/src/filters/append.rs @@ -16,11 +16,15 @@ impl Command for Append { fn signature(&self) -> nu_protocol::Signature { Signature::build("append") - .input_output_types(vec![( - Type::List(Box::new(Type::Any)), - Type::List(Box::new(Type::Any)), - )]) + .input_output_types(vec![ + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + (Type::Record(vec![]), Type::Table(vec![])), + ]) .required("row", SyntaxShape::Any, "the row, list, or table to append") + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/each.rs b/crates/nu-command/src/filters/each.rs index d627b2b018f7..02043c05c454 100644 --- a/crates/nu-command/src/filters/each.rs +++ b/crates/nu-command/src/filters/each.rs @@ -41,6 +41,7 @@ with 'transpose' first."# Type::List(Box::new(Type::Any)), ), (Type::Table(vec![]), Type::List(Box::new(Type::Any))), + (Type::Any, Type::Any), ]) .required( "closure", @@ -48,6 +49,7 @@ with 'transpose' first."# "the closure to run", ) .switch("keep-empty", "keep empty result cells", Some('k')) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/find.rs b/crates/nu-command/src/filters/find.rs index 7a7422155498..5123827a26b3 100644 --- a/crates/nu-command/src/filters/find.rs +++ b/crates/nu-command/src/filters/find.rs @@ -29,7 +29,7 @@ impl Command for Find { Type::List(Box::new(Type::Any)), Type::List(Box::new(Type::Any)), ), - (Type::String, Type::String), + (Type::String, Type::Any), ( // For find -p Type::Table(vec![]), diff --git a/crates/nu-command/src/filters/first.rs b/crates/nu-command/src/filters/first.rs index 153391509d8f..8a9660dac7ce 100644 --- a/crates/nu-command/src/filters/first.rs +++ b/crates/nu-command/src/filters/first.rs @@ -33,12 +33,14 @@ impl Command for First { Type::Any, ), (Type::Binary, Type::Binary), + (Type::Range, Type::Any), ]) .optional( "rows", SyntaxShape::Int, "starting from the front, the number of rows to return", ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/get.rs b/crates/nu-command/src/filters/get.rs index 1a5be9fb962e..f9eb66e26e36 100644 --- a/crates/nu-command/src/filters/get.rs +++ b/crates/nu-command/src/filters/get.rs @@ -34,6 +34,7 @@ If multiple cell paths are given, this will produce a list of values."# Type::Any, ), (Type::Table(vec![]), Type::Any), + (Type::Record(vec![]), Type::Any), ]) .required( "cell_path", @@ -51,6 +52,7 @@ If multiple cell paths are given, this will produce a list of values."# "get path in a case sensitive manner", Some('s'), ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/insert.rs b/crates/nu-command/src/filters/insert.rs index 43840cdb6a68..14c4a65cada5 100644 --- a/crates/nu-command/src/filters/insert.rs +++ b/crates/nu-command/src/filters/insert.rs @@ -19,6 +19,10 @@ impl Command for Insert { .input_output_types(vec![ (Type::Record(vec![]), Type::Record(vec![])), (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), ]) .required( "field", @@ -30,6 +34,7 @@ impl Command for Insert { SyntaxShape::Any, "the new value to give the cell(s)", ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/select.rs b/crates/nu-command/src/filters/select.rs index d40d33709cda..0828c3d20995 100644 --- a/crates/nu-command/src/filters/select.rs +++ b/crates/nu-command/src/filters/select.rs @@ -21,6 +21,7 @@ impl Command for Select { .input_output_types(vec![ (Type::Record(vec![]), Type::Record(vec![])), (Type::Table(vec![]), Type::Table(vec![])), + (Type::List(Box::new(Type::Any)), Type::Any), ]) .switch( "ignore-errors", @@ -32,6 +33,7 @@ impl Command for Select { SyntaxShape::CellPath, "the columns to select from the table", ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/sort_by.rs b/crates/nu-command/src/filters/sort_by.rs index c606e0354093..7e1b0483009f 100644 --- a/crates/nu-command/src/filters/sort_by.rs +++ b/crates/nu-command/src/filters/sort_by.rs @@ -16,7 +16,13 @@ impl Command for SortBy { fn signature(&self) -> nu_protocol::Signature { Signature::build("sort-by") - .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ]) .rest("columns", SyntaxShape::Any, "the column(s) to sort by") .switch("reverse", "Sort in reverse order", Some('r')) .switch( @@ -29,6 +35,7 @@ impl Command for SortBy { "Sort alphanumeric string-based columns naturally (1, 9, 10, 99, 100, ...)", Some('n'), ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/transpose.rs b/crates/nu-command/src/filters/transpose.rs index 1092161a087a..ede1b1c94201 100644 --- a/crates/nu-command/src/filters/transpose.rs +++ b/crates/nu-command/src/filters/transpose.rs @@ -29,6 +29,7 @@ impl Command for Transpose { .input_output_types(vec![ (Type::Table(vec![]), Type::Table(vec![])), (Type::Table(vec![]), Type::Record(vec![])), + (Type::Record(vec![]), Type::Table(vec![])), ]) .switch( "header-row", @@ -55,6 +56,7 @@ impl Command for Transpose { "on repetition of record fields due to `header-row`, keep all the values obtained", Some('a'), ) + .allow_variants_without_examples(true) .rest( "rest", SyntaxShape::String, diff --git a/crates/nu-command/src/filters/uniq_by.rs b/crates/nu-command/src/filters/uniq_by.rs index c1e65e6a6a38..16bbd3b497bf 100644 --- a/crates/nu-command/src/filters/uniq_by.rs +++ b/crates/nu-command/src/filters/uniq_by.rs @@ -17,7 +17,13 @@ impl Command for UniqBy { fn signature(&self) -> Signature { Signature::build("uniq-by") - .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ]) .rest("columns", SyntaxShape::Any, "the column(s) to filter by") .switch( "count", @@ -39,6 +45,7 @@ impl Command for UniqBy { "Return the input values that occur once only", Some('u'), ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/update.rs b/crates/nu-command/src/filters/update.rs index c4daced13bc0..7743252f598c 100644 --- a/crates/nu-command/src/filters/update.rs +++ b/crates/nu-command/src/filters/update.rs @@ -19,6 +19,10 @@ impl Command for Update { .input_output_types(vec![ (Type::Record(vec![]), Type::Record(vec![])), (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), ]) .required( "field", @@ -30,6 +34,7 @@ impl Command for Update { SyntaxShape::Any, "the new value to give the cell(s), or a closure to create the value", ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/upsert.rs b/crates/nu-command/src/filters/upsert.rs index 1c0fcaac5dc8..cbabfe2807f5 100644 --- a/crates/nu-command/src/filters/upsert.rs +++ b/crates/nu-command/src/filters/upsert.rs @@ -19,6 +19,10 @@ impl Command for Upsert { .input_output_types(vec![ (Type::Record(vec![]), Type::Record(vec![])), (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), ]) .required( "field", @@ -30,6 +34,7 @@ impl Command for Upsert { SyntaxShape::Any, "the new value to give the cell(s), or a closure to create the value", ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/where_.rs b/crates/nu-command/src/filters/where_.rs index a34651cc3407..e8d9b0de7bdc 100644 --- a/crates/nu-command/src/filters/where_.rs +++ b/crates/nu-command/src/filters/where_.rs @@ -32,12 +32,14 @@ not supported."# Type::List(Box::new(Type::Any)), ), (Type::Table(vec![]), Type::Table(vec![])), + (Type::Range, Type::Any), ]) .required( "row_condition", SyntaxShape::RowCondition, "Filter condition", ) + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/wrap.rs b/crates/nu-command/src/filters/wrap.rs index 3cee9da09566..64f5be8df49e 100644 --- a/crates/nu-command/src/filters/wrap.rs +++ b/crates/nu-command/src/filters/wrap.rs @@ -23,8 +23,10 @@ impl Command for Wrap { .input_output_types(vec![ (Type::List(Box::new(Type::Any)), Type::Table(vec![])), (Type::Range, Type::Table(vec![])), + (Type::Any, Type::Record(vec![])), ]) .required("name", SyntaxShape::String, "the name of the column") + .allow_variants_without_examples(true) .category(Category::Filters) } diff --git a/crates/nu-command/src/math/abs.rs b/crates/nu-command/src/math/abs.rs index a4d1e8f623aa..2eeecb30b518 100644 --- a/crates/nu-command/src/math/abs.rs +++ b/crates/nu-command/src/math/abs.rs @@ -12,8 +12,15 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math abs") - .input_output_types(vec![(Type::Number, Type::Number)]) + .input_output_types(vec![ + (Type::Number, Type::Number), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/avg.rs b/crates/nu-command/src/math/avg.rs index 95cce92306a3..1deae7ee4ba2 100644 --- a/crates/nu-command/src/math/avg.rs +++ b/crates/nu-command/src/math/avg.rs @@ -14,7 +14,12 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math avg") - .input_output_types(vec![(Type::List(Box::new(Type::Number)), Type::Number)]) + .input_output_types(vec![ + (Type::List(Box::new(Type::Number)), Type::Number), + (Type::List(Box::new(Type::Duration)), Type::Duration), + (Type::List(Box::new(Type::Filesize)), Type::Filesize), + ]) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/ceil.rs b/crates/nu-command/src/math/ceil.rs index 7398b957a7c9..ae7f18173973 100644 --- a/crates/nu-command/src/math/ceil.rs +++ b/crates/nu-command/src/math/ceil.rs @@ -12,8 +12,15 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math ceil") - .input_output_types(vec![(Type::Number, Type::Int)]) + .input_output_types(vec![ + (Type::Number, Type::Int), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/floor.rs b/crates/nu-command/src/math/floor.rs index e605dfa8ae8d..5625417bf17e 100644 --- a/crates/nu-command/src/math/floor.rs +++ b/crates/nu-command/src/math/floor.rs @@ -12,8 +12,15 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math floor") - .input_output_types(vec![(Type::Number, Type::Int)]) + .input_output_types(vec![ + (Type::Number, Type::Int), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/log.rs b/crates/nu-command/src/math/log.rs index 257857ce3d86..1276b8d17dca 100644 --- a/crates/nu-command/src/math/log.rs +++ b/crates/nu-command/src/math/log.rs @@ -20,7 +20,15 @@ impl Command for SubCommand { SyntaxShape::Number, "Base for which the logarithm should be computed", ) - .input_output_types(vec![(Type::Number, Type::Float)]) + .input_output_types(vec![ + (Type::Number, Type::Float), + (Type::Number, Type::Int), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + ]) + .allow_variants_without_examples(true) .vectorizes_over_list(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/max.rs b/crates/nu-command/src/math/max.rs index 1d6e18358385..fdfc99394645 100644 --- a/crates/nu-command/src/math/max.rs +++ b/crates/nu-command/src/math/max.rs @@ -18,6 +18,7 @@ impl Command for SubCommand { (Type::List(Box::new(Type::Number)), Type::Number), (Type::Table(vec![]), Type::Record(vec![])), ]) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/median.rs b/crates/nu-command/src/math/median.rs index 2fd925af53aa..02c5eb0d1d05 100644 --- a/crates/nu-command/src/math/median.rs +++ b/crates/nu-command/src/math/median.rs @@ -20,6 +20,7 @@ impl Command for SubCommand { (Type::List(Box::new(Type::Number)), Type::Number), (Type::Table(vec![]), Type::Record(vec![])), ]) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/min.rs b/crates/nu-command/src/math/min.rs index bfe10c6b31dc..2cb34581255f 100644 --- a/crates/nu-command/src/math/min.rs +++ b/crates/nu-command/src/math/min.rs @@ -18,6 +18,7 @@ impl Command for SubCommand { (Type::List(Box::new(Type::Number)), Type::Number), (Type::Table(vec![]), Type::Record(vec![])), ]) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/mode.rs b/crates/nu-command/src/math/mode.rs index ca0fd477f3a6..4f1de19d4375 100644 --- a/crates/nu-command/src/math/mode.rs +++ b/crates/nu-command/src/math/mode.rs @@ -45,6 +45,7 @@ impl Command for SubCommand { ), (Type::Table(vec![]), Type::Record(vec![])), ]) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/round.rs b/crates/nu-command/src/math/round.rs index ad4c378bec71..b595688e11e9 100644 --- a/crates/nu-command/src/math/round.rs +++ b/crates/nu-command/src/math/round.rs @@ -15,7 +15,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math round") - .input_output_types(vec![(Type::Number, Type::Number)]) + .input_output_types(vec![ + (Type::Number, Type::Number), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + ]) .vectorizes_over_list(true) .named( "precision", diff --git a/crates/nu-command/src/math/sqrt.rs b/crates/nu-command/src/math/sqrt.rs index d34199915114..f67f42b06cd2 100644 --- a/crates/nu-command/src/math/sqrt.rs +++ b/crates/nu-command/src/math/sqrt.rs @@ -12,8 +12,15 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math sqrt") - .input_output_types(vec![(Type::Number, Type::Number)]) + .input_output_types(vec![ + (Type::Number, Type::Number), + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/math/sum.rs b/crates/nu-command/src/math/sum.rs index 60d91a543fab..ed9d27012cd8 100644 --- a/crates/nu-command/src/math/sum.rs +++ b/crates/nu-command/src/math/sum.rs @@ -14,7 +14,12 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math sum") - .input_output_types(vec![(Type::List(Box::new(Type::Number)), Type::Number)]) + .input_output_types(vec![ + (Type::List(Box::new(Type::Number)), Type::Number), + (Type::Range, Type::Number), + (Type::Table(vec![]), Type::Table(vec![])), + ]) + .allow_variants_without_examples(true) .category(Category::Math) } diff --git a/crates/nu-command/src/network/url/encode.rs b/crates/nu-command/src/network/url/encode.rs index ca13ff5015f1..0555fabe4405 100644 --- a/crates/nu-command/src/network/url/encode.rs +++ b/crates/nu-command/src/network/url/encode.rs @@ -17,7 +17,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("url encode") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![(Type::String, Type::String), (Type::List(Box::new(Type::String)), Type::List(Box::new(Type::String)))]) .vectorizes_over_list(true) .switch( "all", diff --git a/crates/nu-command/src/path/join.rs b/crates/nu-command/src/path/join.rs index 603a9a0c0474..d5a622c3135f 100644 --- a/crates/nu-command/src/path/join.rs +++ b/crates/nu-command/src/path/join.rs @@ -35,6 +35,7 @@ impl Command for SubCommand { .input_output_types(vec![ (Type::String, Type::String), (Type::List(Box::new(Type::String)), Type::String), + (Type::Record(vec![]), Type::String), (Type::Table(vec![]), Type::List(Box::new(Type::String))), ]) .named( @@ -43,6 +44,7 @@ impl Command for SubCommand { "For a record or table input, join strings at the given columns", Some('c'), ) + .allow_variants_without_examples(true) .rest("append", SyntaxShape::String, "Path to append to the input") } diff --git a/crates/nu-command/src/platform/ansi/strip.rs b/crates/nu-command/src/platform/ansi/strip.rs index 54f2bdfabc34..9c38e832a28a 100644 --- a/crates/nu-command/src/platform/ansi/strip.rs +++ b/crates/nu-command/src/platform/ansi/strip.rs @@ -15,12 +15,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("ansi strip") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![(Type::String, Type::String), (Type::List(Box::new(Type::String)), Type::List(Box::new(Type::String)))]) .rest( "cell path", SyntaxShape::CellPath, "for a data structure input, remove ANSI sequences from strings at the given cell paths", ) + .allow_variants_without_examples(true) .category(Category::Platform) } diff --git a/crates/nu-command/src/strings/parse.rs b/crates/nu-command/src/strings/parse.rs index ebeeee8a71d8..f74fd4237146 100644 --- a/crates/nu-command/src/strings/parse.rs +++ b/crates/nu-command/src/strings/parse.rs @@ -30,8 +30,12 @@ impl Command for Parse { SyntaxShape::String, "the pattern to match. Eg) \"{foo}: {bar}\"", ) - .input_output_types(vec![(Type::String, Type::Table(vec![]))]) + .input_output_types(vec![ + (Type::String, Type::Table(vec![])), + (Type::List(Box::new(Type::Any)), Type::Table(vec![])), + ]) .switch("regex", "use full regex syntax for patterns", Some('r')) + .allow_variants_without_examples(true) .category(Category::Strings) } diff --git a/crates/nu-command/src/strings/split/row.rs b/crates/nu-command/src/strings/split/row.rs index 74d5d322c7db..2ee5c83fb4db 100644 --- a/crates/nu-command/src/strings/split/row.rs +++ b/crates/nu-command/src/strings/split/row.rs @@ -16,8 +16,12 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("split row") - .input_output_types(vec![(Type::String, Type::List(Box::new(Type::String)))]) + .input_output_types(vec![ + (Type::String, Type::List(Box::new(Type::String))), + (Type::List(Box::new(Type::String)), Type::Table(vec![])), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .required( "separator", SyntaxShape::String, diff --git a/crates/nu-command/src/strings/str_/case/camel_case.rs b/crates/nu-command/src/strings/str_/case/camel_case.rs index 051599add59f..cee3e1993e2f 100644 --- a/crates/nu-command/src/strings/str_/case/camel_case.rs +++ b/crates/nu-command/src/strings/str_/case/camel_case.rs @@ -17,7 +17,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str camel-case") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/capitalize.rs b/crates/nu-command/src/strings/str_/case/capitalize.rs index 498b77f2bbdb..bad3ddb720ae 100644 --- a/crates/nu-command/src/strings/str_/case/capitalize.rs +++ b/crates/nu-command/src/strings/str_/case/capitalize.rs @@ -15,7 +15,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str capitalize") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/downcase.rs b/crates/nu-command/src/strings/str_/case/downcase.rs index 9c63e29b30de..df16626f4b9e 100644 --- a/crates/nu-command/src/strings/str_/case/downcase.rs +++ b/crates/nu-command/src/strings/str_/case/downcase.rs @@ -15,7 +15,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str downcase") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/kebab_case.rs b/crates/nu-command/src/strings/str_/case/kebab_case.rs index 44c55d124517..f807862e857d 100644 --- a/crates/nu-command/src/strings/str_/case/kebab_case.rs +++ b/crates/nu-command/src/strings/str_/case/kebab_case.rs @@ -17,7 +17,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str kebab-case") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/pascal_case.rs b/crates/nu-command/src/strings/str_/case/pascal_case.rs index 757aab1883f8..459dde3db18d 100644 --- a/crates/nu-command/src/strings/str_/case/pascal_case.rs +++ b/crates/nu-command/src/strings/str_/case/pascal_case.rs @@ -17,7 +17,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str pascal-case") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/screaming_snake_case.rs b/crates/nu-command/src/strings/str_/case/screaming_snake_case.rs index 4e4d6f677e76..840fce560c0d 100644 --- a/crates/nu-command/src/strings/str_/case/screaming_snake_case.rs +++ b/crates/nu-command/src/strings/str_/case/screaming_snake_case.rs @@ -16,7 +16,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str screaming-snake-case") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/snake_case.rs b/crates/nu-command/src/strings/str_/case/snake_case.rs index 137908421ea7..56e7bb2b0930 100644 --- a/crates/nu-command/src/strings/str_/case/snake_case.rs +++ b/crates/nu-command/src/strings/str_/case/snake_case.rs @@ -16,7 +16,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str snake-case") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/title_case.rs b/crates/nu-command/src/strings/str_/case/title_case.rs index 1690c2fa3c21..daab6fd47da7 100644 --- a/crates/nu-command/src/strings/str_/case/title_case.rs +++ b/crates/nu-command/src/strings/str_/case/title_case.rs @@ -17,7 +17,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str title-case") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/case/upcase.rs b/crates/nu-command/src/strings/str_/case/upcase.rs index fd4fdca79045..259026f56b60 100644 --- a/crates/nu-command/src/strings/str_/case/upcase.rs +++ b/crates/nu-command/src/strings/str_/case/upcase.rs @@ -14,8 +14,12 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str upcase") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/strings/str_/contains.rs b/crates/nu-command/src/strings/str_/contains.rs index ef47588fb19d..ce02b46b72f3 100644 --- a/crates/nu-command/src/strings/str_/contains.rs +++ b/crates/nu-command/src/strings/str_/contains.rs @@ -32,6 +32,7 @@ impl Command for SubCommand { .input_output_types(vec![ (Type::String, Type::Bool), (Type::Table(vec![]), Type::Table(vec![])), + (Type::List(Box::new(Type::String)), Type::List(Box::new(Type::Bool))) ]) .vectorizes_over_list(true) .required("string", SyntaxShape::String, "the substring to find") diff --git a/crates/nu-command/src/strings/str_/join.rs b/crates/nu-command/src/strings/str_/join.rs index e48adefacb1e..15e998c94d98 100644 --- a/crates/nu-command/src/strings/str_/join.rs +++ b/crates/nu-command/src/strings/str_/join.rs @@ -16,12 +16,16 @@ impl Command for StrJoin { fn signature(&self) -> Signature { Signature::build("str join") - .input_output_types(vec![(Type::List(Box::new(Type::String)), Type::String)]) + .input_output_types(vec![ + (Type::List(Box::new(Type::Any)), Type::String), + (Type::String, Type::String), + ]) .optional( "separator", SyntaxShape::String, "optional separator to use when creating string", ) + .allow_variants_without_examples(true) .category(Category::Strings) } diff --git a/crates/nu-command/src/strings/str_/length.rs b/crates/nu-command/src/strings/str_/length.rs index 78a90fc5280a..8f6c7c6db90e 100644 --- a/crates/nu-command/src/strings/str_/length.rs +++ b/crates/nu-command/src/strings/str_/length.rs @@ -29,7 +29,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str length") - .input_output_types(vec![(Type::String, Type::Int)]) + .input_output_types(vec![(Type::String, Type::Int), (Type::List(Box::new(Type::String)), Type::List(Box::new(Type::Int)))]) .vectorizes_over_list(true) .switch( "grapheme-clusters", diff --git a/crates/nu-command/src/strings/str_/replace.rs b/crates/nu-command/src/strings/str_/replace.rs index e6918fcd3bc6..7a4ece8e2174 100644 --- a/crates/nu-command/src/strings/str_/replace.rs +++ b/crates/nu-command/src/strings/str_/replace.rs @@ -34,7 +34,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str replace") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) .required("find", SyntaxShape::String, "the pattern to find") .required("replace", SyntaxShape::String, "the replacement string") diff --git a/crates/nu-command/src/strings/str_/reverse.rs b/crates/nu-command/src/strings/str_/reverse.rs index 22586be55ed1..89209d502890 100644 --- a/crates/nu-command/src/strings/str_/reverse.rs +++ b/crates/nu-command/src/strings/str_/reverse.rs @@ -16,7 +16,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str reverse") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + ( + Type::List(Box::new(Type::String)), + Type::List(Box::new(Type::String)), + ), + ]) .vectorizes_over_list(true) .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/substring.rs b/crates/nu-command/src/strings/str_/substring.rs index c94b0edd5f48..6079f64cc62f 100644 --- a/crates/nu-command/src/strings/str_/substring.rs +++ b/crates/nu-command/src/strings/str_/substring.rs @@ -42,8 +42,9 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str substring") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![(Type::String, Type::String), (Type::Table(vec![]), Type::Table(vec![]))]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .switch( "grapheme-clusters", "count indexes and split using grapheme clusters (all visible chars have length 1)", diff --git a/crates/nu-command/src/strings/str_/trim/trim_.rs b/crates/nu-command/src/strings/str_/trim/trim_.rs index 6b7c8888d211..e2b80ab88d3f 100644 --- a/crates/nu-command/src/strings/str_/trim/trim_.rs +++ b/crates/nu-command/src/strings/str_/trim/trim_.rs @@ -35,8 +35,16 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str trim") - .input_output_types(vec![(Type::String, Type::String)]) + .input_output_types(vec![ + (Type::String, Type::String), + ( + Type::List(Box::new(Type::String)), + Type::List(Box::new(Type::String)), + ), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .vectorizes_over_list(true) + .allow_variants_without_examples(true) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/tests/commands/drop.rs b/crates/nu-command/tests/commands/drop.rs index 73d7e0c51942..ecba9177d24f 100644 --- a/crates/nu-command/tests/commands/drop.rs +++ b/crates/nu-command/tests/commands/drop.rs @@ -91,5 +91,5 @@ fn nth_missing_first_argument() { fn fail_on_non_iterator() { let actual = nu!("1 | drop 50"); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/last.rs b/crates/nu-command/tests/commands/last.rs index 2ff1254affbd..f84225b95019 100644 --- a/crates/nu-command/tests/commands/last.rs +++ b/crates/nu-command/tests/commands/last.rs @@ -78,7 +78,7 @@ fn last_errors_on_negative_index() { fn fail_on_non_iterator() { let actual = nu!("1 | last"); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } #[test] diff --git a/crates/nu-command/tests/commands/math/round.rs b/crates/nu-command/tests/commands/math/round.rs index 6b93ebd08375..5d2158a56f7d 100644 --- a/crates/nu-command/tests/commands/math/round.rs +++ b/crates/nu-command/tests/commands/math/round.rs @@ -32,5 +32,5 @@ fn can_round_float_with_negative_precision() { fn fails_with_wrong_input_type() { let actual = nu!("\"not_a_number\" | math round"); - assert!(actual.err.contains("Input type not supported")) + assert!(actual.err.contains("command doesn't support")) } diff --git a/crates/nu-command/tests/commands/mkdir.rs b/crates/nu-command/tests/commands/mkdir.rs index 250e7bdbce00..7943ce77665a 100644 --- a/crates/nu-command/tests/commands/mkdir.rs +++ b/crates/nu-command/tests/commands/mkdir.rs @@ -70,7 +70,6 @@ fn print_created_paths() { pipeline( r#" mkdir -v dir_1 dir_2 dir_3 - | length "# )); diff --git a/crates/nu-command/tests/commands/rename.rs b/crates/nu-command/tests/commands/rename.rs index 19d733f70c1b..50fc149458aa 100644 --- a/crates/nu-command/tests/commands/rename.rs +++ b/crates/nu-command/tests/commands/rename.rs @@ -83,7 +83,7 @@ fn errors_if_no_columns_present() { "# )); - assert!(actual.err.contains("only record input data is supported")); + assert!(actual.err.contains("command doesn't support")); }) } diff --git a/crates/nu-command/tests/commands/reverse.rs b/crates/nu-command/tests/commands/reverse.rs index e41459800f55..936cf9f9e428 100644 --- a/crates/nu-command/tests/commands/reverse.rs +++ b/crates/nu-command/tests/commands/reverse.rs @@ -14,5 +14,5 @@ fn can_get_reverse_first() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline("1 | reverse")); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/skip/skip_.rs b/crates/nu-command/tests/commands/skip/skip_.rs index ac461523b5c2..6b86843b2268 100644 --- a/crates/nu-command/tests/commands/skip/skip_.rs +++ b/crates/nu-command/tests/commands/skip/skip_.rs @@ -19,5 +19,5 @@ fn binary_skip() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline("1 | skip 2")); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/skip/until.rs b/crates/nu-command/tests/commands/skip/until.rs index e6acc9c5cde3..7925cf4bbd0c 100644 --- a/crates/nu-command/tests/commands/skip/until.rs +++ b/crates/nu-command/tests/commands/skip/until.rs @@ -54,5 +54,5 @@ fn condition_is_met() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline("1 | skip until {|row| $row == 2}")); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/skip/while_.rs b/crates/nu-command/tests/commands/skip/while_.rs index b008ba4dad54..15f9b6d3e12a 100644 --- a/crates/nu-command/tests/commands/skip/while_.rs +++ b/crates/nu-command/tests/commands/skip/while_.rs @@ -54,5 +54,5 @@ fn condition_is_met() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline("1 | skip while {|row| $row == 2}")); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/sort_by.rs b/crates/nu-command/tests/commands/sort_by.rs index 0d7165b2fdd1..d1c48dd7bf67 100644 --- a/crates/nu-command/tests/commands/sort_by.rs +++ b/crates/nu-command/tests/commands/sort_by.rs @@ -126,5 +126,5 @@ fn no_column_specified_fails() { fn fail_on_non_iterator() { let actual = nu!("1 | sort-by"); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/take/rows.rs b/crates/nu-command/tests/commands/take/rows.rs index 3f57a5911c22..a8099abf9138 100644 --- a/crates/nu-command/tests/commands/take/rows.rs +++ b/crates/nu-command/tests/commands/take/rows.rs @@ -51,7 +51,7 @@ fn fails_on_string() { "# )); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } #[test] diff --git a/crates/nu-command/tests/commands/take/until.rs b/crates/nu-command/tests/commands/take/until.rs index 5a6aaebc7099..0859abe5fa1a 100644 --- a/crates/nu-command/tests/commands/take/until.rs +++ b/crates/nu-command/tests/commands/take/until.rs @@ -55,5 +55,5 @@ fn condition_is_met() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline("1 | take until {|row| $row == 2}")); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/take/while_.rs b/crates/nu-command/tests/commands/take/while_.rs index 33e052ffdc45..07ad8a081913 100644 --- a/crates/nu-command/tests/commands/take/while_.rs +++ b/crates/nu-command/tests/commands/take/while_.rs @@ -54,5 +54,5 @@ fn condition_is_met() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline("1 | take while {|row| $row == 2}")); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } diff --git a/crates/nu-command/tests/commands/where_.rs b/crates/nu-command/tests/commands/where_.rs index 1f1ccc764bb2..d74d3f1dd37f 100644 --- a/crates/nu-command/tests/commands/where_.rs +++ b/crates/nu-command/tests/commands/where_.rs @@ -180,7 +180,7 @@ fn contains_operator() { fn fail_on_non_iterator() { let actual = nu!(cwd: ".", pipeline(r#"{"name": "foo", "size": 3} | where name == "foo""#)); - assert!(actual.err.contains("only_supports_this_input_type")); + assert!(actual.err.contains("command doesn't support")); } // Test that filtering on columns that might be missing/null works diff --git a/crates/nu-command/tests/format_conversions/csv.rs b/crates/nu-command/tests/format_conversions/csv.rs index cacb796f297e..b120d0a213c7 100644 --- a/crates/nu-command/tests/format_conversions/csv.rs +++ b/crates/nu-command/tests/format_conversions/csv.rs @@ -405,5 +405,5 @@ fn string_to_csv_error() { "# )); - assert!(actual.err.contains("can't convert")) + assert!(actual.err.contains("command doesn't support")) } diff --git a/crates/nu-command/tests/format_conversions/toml.rs b/crates/nu-command/tests/format_conversions/toml.rs index e0e220e31e3c..d69f77fbed07 100644 --- a/crates/nu-command/tests/format_conversions/toml.rs +++ b/crates/nu-command/tests/format_conversions/toml.rs @@ -70,7 +70,7 @@ fn table_to_toml_fails() { "# )); - assert_eq!(actual.out, "true"); + assert!(actual.err.contains("command doesn't support")); } #[test] @@ -83,7 +83,7 @@ fn string_to_toml_fails() { "# )); - assert_eq!(actual.out, "true"); + assert!(actual.err.contains("command doesn't support")); } #[test] diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 3a4a318624e4..d0e1edfa3bcd 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -4,7 +4,7 @@ use crate::{ lite_parser::{lite_parse, LiteCommand, LiteElement, LitePipeline}, parse_mut, parse_patterns::{parse_match_pattern, parse_pattern}, - type_check::{math_result_type, type_compatible}, + type_check::{self, math_result_type, type_compatible}, Token, TokenContents, }; @@ -5582,6 +5582,8 @@ pub fn parse_block( block.span = Some(span); + type_check::check_block_input_output(working_set, &block); + block } diff --git a/crates/nu-parser/src/type_check.rs b/crates/nu-parser/src/type_check.rs index bb4f4e3a2dfa..f09d1e3b04af 100644 --- a/crates/nu-parser/src/type_check.rs +++ b/crates/nu-parser/src/type_check.rs @@ -1,5 +1,8 @@ use nu_protocol::{ - ast::{Bits, Boolean, Comparison, Expr, Expression, Math, Operator}, + ast::{ + Bits, Block, Boolean, Comparison, Expr, Expression, Math, Operator, Pipeline, + PipelineElement, + }, engine::StateWorkingSet, ParseError, Type, }; @@ -24,7 +27,30 @@ pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool { match (lhs, rhs) { (Type::List(c), Type::List(d)) => type_compatible(c, d), - (Type::List(c), Type::Table(_)) => matches!(**c, Type::Any), + (Type::ListStream, Type::List(_)) => true, + (Type::List(_), Type::ListStream) => true, + (Type::List(c), Type::Table(table_fields)) => { + if matches!(**c, Type::Any) { + return true; + } + + if let Type::Record(fields) = &**c { + is_compatible(fields, table_fields) + } else { + false + } + } + (Type::Table(table_fields), Type::List(c)) => { + if matches!(**c, Type::Any) { + return true; + } + + if let Type::Record(fields) = &**c { + is_compatible(table_fields, fields) + } else { + false + } + } (Type::Number, Type::Int) => true, (Type::Int, Type::Number) => true, (Type::Number, Type::Float) => true, @@ -921,3 +947,108 @@ pub fn math_result_type( } } } + +pub fn check_pipeline_type( + working_set: &mut StateWorkingSet, + pipeline: &Pipeline, + input_type: Type, +) -> Type { + let mut current_type = input_type; + + 'elem: for elem in &pipeline.elements { + match elem { + PipelineElement::Expression( + _, + Expression { + expr: Expr::Call(call), + .. + }, + ) => { + let decl = working_set.get_decl(call.decl_id); + + if current_type == Type::Any { + let mut new_current_type = None; + for (_, call_output) in decl.signature().input_output_types { + if let Some(inner_current_type) = &new_current_type { + if inner_current_type == &Type::Any { + break; + } else if inner_current_type != &call_output { + // Union unequal types to Any for now + new_current_type = Some(Type::Any) + } + } else { + new_current_type = Some(call_output.clone()) + } + } + + if let Some(new_current_type) = new_current_type { + current_type = new_current_type + } else { + current_type = Type::Any; + } + continue 'elem; + } else { + for (call_input, call_output) in decl.signature().input_output_types { + if type_compatible(&call_input, ¤t_type) { + current_type = call_output.clone(); + continue 'elem; + } + } + } + + if !decl.signature().input_output_types.is_empty() { + working_set.error(ParseError::InputMismatch(current_type, call.head)) + } + current_type = Type::Any; + } + PipelineElement::Expression(_, Expression { ty, .. }) => { + current_type = ty.clone(); + } + _ => { + current_type = Type::Any; + } + } + } + + current_type +} + +pub fn check_block_input_output(working_set: &mut StateWorkingSet, block: &Block) { + // let inputs = block.input_types(); + + for (input_type, output_type) in &block.signature.input_output_types { + let mut current_type = input_type.clone(); + let mut current_output_type = Type::Nothing; + + for pipeline in &block.pipelines { + current_output_type = check_pipeline_type(working_set, pipeline, current_type); + current_type = Type::Nothing; + } + + if !type_compatible(output_type, ¤t_output_type) + && output_type != &Type::Any + && current_output_type != Type::Any + { + working_set.error(ParseError::OutputMismatch( + output_type.clone(), + block + .pipelines + .last() + .expect("internal error: we should have pipelines") + .elements + .last() + .expect("internal error: we should have elements") + .span(), + )) + } + } + + if block.signature.input_output_types.is_empty() { + let mut current_type = Type::Any; + + for pipeline in &block.pipelines { + let _ = check_pipeline_type(working_set, pipeline, current_type); + current_type = Type::Nothing; + } + } +} diff --git a/crates/nu-protocol/src/ast/block.rs b/crates/nu-protocol/src/ast/block.rs index e57171db0e95..0840cf642c3e 100644 --- a/crates/nu-protocol/src/ast/block.rs +++ b/crates/nu-protocol/src/ast/block.rs @@ -1,5 +1,5 @@ -use super::{Expr, Expression, Pipeline}; -use crate::{ast::PipelineElement, engine::StateWorkingSet, Signature, Span, Type, VarId}; +use super::Pipeline; +use crate::{ast::PipelineElement, Signature, Span, Type, VarId}; use serde::{Deserialize, Serialize}; use std::ops::{Index, IndexMut}; @@ -66,37 +66,6 @@ impl Block { } } - pub fn input_type(&self, working_set: &StateWorkingSet) -> Type { - if let Some(first) = self.pipelines.first() { - if let Some(first) = first.elements.first() { - match first { - PipelineElement::Expression( - _, - Expression { - expr: Expr::Call(call), - .. - }, - ) => { - let decl = working_set.get_decl(call.decl_id); - - decl.signature().get_input_type() - } - PipelineElement::Expression( - _, - Expression { - expr: Expr::ExternalCall(..), - .. - }, - ) => Type::Any, - _ => Type::Nothing, - } - } else { - Type::Nothing - } - } else { - Type::Nothing - } - } pub fn output_type(&self) -> Type { if let Some(last) = self.pipelines.last() { if let Some(last) = last.elements.last() { diff --git a/crates/nu-protocol/src/parse_error.rs b/crates/nu-protocol/src/parse_error.rs index e7f3f834ef26..610f513499a8 100644 --- a/crates/nu-protocol/src/parse_error.rs +++ b/crates/nu-protocol/src/parse_error.rs @@ -48,6 +48,14 @@ pub enum ParseError { #[diagnostic(code(nu::parser::parse_mismatch_with_full_string_msg))] ExpectedWithStringMsg(String, #[label("expected {0}")] Span), + #[error("Command does not support {0} input.")] + #[diagnostic(code(nu::parser::input_type_mismatch))] + InputMismatch(Type, #[label("command doesn't support {0} input")] Span), + + #[error("Command output doesn't match {0}.")] + #[diagnostic(code(nu::parser::output_type_mismatch))] + OutputMismatch(Type, #[label("command doesn't output {0}")] Span), + #[error("Type mismatch during operation.")] #[diagnostic(code(nu::parser::type_mismatch))] Mismatch(String, String, #[label("expected {0}, found {1}")] Span), // expected, found, span @@ -526,6 +534,8 @@ impl ParseError { ParseError::KeywordMissingArgument(_, _, s) => *s, ParseError::MissingType(s) => *s, ParseError::TypeMismatch(_, _, s) => *s, + ParseError::InputMismatch(_, s) => *s, + ParseError::OutputMismatch(_, s) => *s, ParseError::MissingRequiredFlag(_, s) => *s, ParseError::IncompleteMathExpression(s) => *s, ParseError::UnknownState(_, s) => *s, diff --git a/src/tests/test_bits.rs b/src/tests/test_bits.rs index 4399ea711aa7..ea4231786cc2 100644 --- a/src/tests/test_bits.rs +++ b/src/tests/test_bits.rs @@ -46,7 +46,7 @@ fn bits_xor_negative() -> TestResult { #[test] fn bits_xor_list() -> TestResult { run_test( - "[1 2 3 8 9 10] | bits xor 2 | str join '.'", + "[1 2 3 8 9 10] | bits xor 2 | into string | str join '.'", "3.0.1.10.11.8", ) } diff --git a/src/tests/test_engine.rs b/src/tests/test_engine.rs index 39ae57cf6802..4aa2b7453bd5 100644 --- a/src/tests/test_engine.rs +++ b/src/tests/test_engine.rs @@ -55,7 +55,7 @@ fn in_and_if_else() -> TestResult { #[test] fn help_works_with_missing_requirements() -> TestResult { // `each while` is part of the *extra* feature and adds 3 lines - let expected_length = if cfg!(feature = "extra") { "65" } else { "62" }; + let expected_length = if cfg!(feature = "extra") { "66" } else { "63" }; run_test(r#"each --help | lines | length"#, expected_length) }