From df94052180f45685b2a04ba332da734aa8aa701f Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Wed, 9 Nov 2022 16:55:05 -0500 Subject: [PATCH] Declare input and output types of commands (#6796) * Add failing test that list of ints and floats is List * Start defining subtype relation * Make it possible to declare input and output types for commands - Enforce them in tests * Declare input and output types of commands * Add formatted signatures to `help commands` table * Revert SyntaxShape::Table -> Type::Table change * Revert unnecessary derive(Hash) on SyntaxShape Co-authored-by: JT <547158+jntrnr@users.noreply.github.com> --- Cargo.lock | 2 + crates/nu-command/src/bits/and.rs | 4 +- crates/nu-command/src/bits/not.rs | 4 +- crates/nu-command/src/bits/or.rs | 4 +- crates/nu-command/src/bits/rotate_left.rs | 4 +- crates/nu-command/src/bits/rotate_right.rs | 4 +- crates/nu-command/src/bits/shift_left.rs | 4 +- crates/nu-command/src/bits/shift_right.rs | 4 +- crates/nu-command/src/bits/xor.rs | 4 +- crates/nu-command/src/bytes/add.rs | 4 +- crates/nu-command/src/bytes/at.rs | 4 +- crates/nu-command/src/bytes/build_.rs | 3 +- crates/nu-command/src/bytes/collect.rs | 3 +- crates/nu-command/src/bytes/ends_with.rs | 3 +- crates/nu-command/src/bytes/index_of.rs | 6 +- crates/nu-command/src/bytes/length.rs | 4 +- crates/nu-command/src/bytes/remove.rs | 4 +- crates/nu-command/src/bytes/replace.rs | 4 +- crates/nu-command/src/bytes/reverse.rs | 3 +- crates/nu-command/src/bytes/starts_with.rs | 3 +- .../nu-command/src/charting/hashable_value.rs | 2 +- crates/nu-command/src/charting/histogram.rs | 43 +- crates/nu-command/src/conversions/fmt.rs | 6 +- .../nu-command/src/conversions/into/binary.rs | 12 +- .../nu-command/src/conversions/into/bool.rs | 14 +- .../src/conversions/into/datetime.rs | 8 +- .../src/conversions/into/decimal.rs | 17 +- .../src/conversions/into/duration.rs | 21 +- .../src/conversions/into/filesize.rs | 8 +- crates/nu-command/src/conversions/into/int.rs | 12 +- .../nu-command/src/conversions/into/string.rs | 27 +- crates/nu-command/src/core_commands/alias.rs | 7 +- crates/nu-command/src/core_commands/ast.rs | 10 +- .../src/core_commands/commandline.rs | 3 +- crates/nu-command/src/core_commands/debug.rs | 30 +- crates/nu-command/src/core_commands/def.rs | 3 +- .../nu-command/src/core_commands/def_env.rs | 3 +- .../nu-command/src/core_commands/describe.rs | 6 +- crates/nu-command/src/core_commands/do_.rs | 12 +- crates/nu-command/src/core_commands/echo.rs | 4 +- crates/nu-command/src/core_commands/export.rs | 6 +- .../src/core_commands/export_alias.rs | 3 +- .../src/core_commands/export_def.rs | 3 +- .../src/core_commands/export_def_env.rs | 3 +- .../src/core_commands/export_extern.rs | 3 +- .../src/core_commands/export_use.rs | 3 +- .../nu-command/src/core_commands/extern_.rs | 3 +- crates/nu-command/src/core_commands/for_.rs | 3 +- crates/nu-command/src/core_commands/help.rs | 24 +- crates/nu-command/src/core_commands/hide.rs | 3 +- .../nu-command/src/core_commands/hide_env.rs | 3 +- crates/nu-command/src/core_commands/if_.rs | 3 +- crates/nu-command/src/core_commands/ignore.rs | 8 +- crates/nu-command/src/core_commands/let_.rs | 4 +- .../nu-command/src/core_commands/metadata.rs | 4 +- crates/nu-command/src/core_commands/module.rs | 3 +- .../nu-command/src/core_commands/register.rs | 3 +- crates/nu-command/src/core_commands/use_.rs | 3 +- .../nu-command/src/core_commands/version.rs | 14 +- crates/nu-command/src/date/format.rs | 28 +- crates/nu-command/src/date/humanize.rs | 30 +- crates/nu-command/src/date/list_timezone.rs | 6 +- crates/nu-command/src/date/now.rs | 6 +- crates/nu-command/src/date/to_record.rs | 79 ++-- crates/nu-command/src/date/to_table.rs | 81 ++-- crates/nu-command/src/date/to_timezone.rs | 33 +- crates/nu-command/src/env/export_env.rs | 22 +- crates/nu-command/src/env/with_env.rs | 3 +- crates/nu-command/src/example_test.rs | 382 +++++++++++++----- crates/nu-command/src/filters/all.rs | 7 +- crates/nu-command/src/filters/any.rs | 7 +- crates/nu-command/src/filters/append.rs | 6 +- crates/nu-command/src/filters/collect.rs | 3 +- crates/nu-command/src/filters/columns.rs | 9 +- crates/nu-command/src/filters/compact.rs | 15 +- crates/nu-command/src/filters/default.rs | 7 +- crates/nu-command/src/filters/drop/column.rs | 5 +- crates/nu-command/src/filters/drop/drop_.rs | 35 +- crates/nu-command/src/filters/drop/nth.rs | 6 +- crates/nu-command/src/filters/each.rs | 6 +- crates/nu-command/src/filters/each_while.rs | 6 +- crates/nu-command/src/filters/empty.rs | 3 +- crates/nu-command/src/filters/every.rs | 6 +- crates/nu-command/src/filters/find.rs | 16 +- crates/nu-command/src/filters/first.rs | 18 + crates/nu-command/src/filters/flatten.rs | 9 +- crates/nu-command/src/filters/get.rs | 41 +- crates/nu-command/src/filters/group.rs | 10 +- crates/nu-command/src/filters/group_by.rs | 21 +- crates/nu-command/src/filters/headers.rs | 14 +- crates/nu-command/src/filters/insert.rs | 14 +- crates/nu-command/src/filters/last.rs | 19 +- crates/nu-command/src/filters/length.rs | 6 +- crates/nu-command/src/filters/lines.rs | 3 +- crates/nu-command/src/filters/merge.rs | 26 +- crates/nu-command/src/filters/move_.rs | 6 +- crates/nu-command/src/filters/par_each.rs | 6 +- crates/nu-command/src/filters/prepend.rs | 6 +- crates/nu-command/src/filters/range.rs | 6 +- crates/nu-command/src/filters/reduce.rs | 3 +- crates/nu-command/src/filters/reject.rs | 26 +- crates/nu-command/src/filters/rename.rs | 15 +- crates/nu-command/src/filters/reverse.rs | 53 ++- .../nu-command/src/filters/roll/roll_down.rs | 6 +- .../nu-command/src/filters/roll/roll_left.rs | 3 +- .../nu-command/src/filters/roll/roll_right.rs | 3 +- crates/nu-command/src/filters/roll/roll_up.rs | 4 +- crates/nu-command/src/filters/rotate.rs | 3 +- crates/nu-command/src/filters/select.rs | 31 +- crates/nu-command/src/filters/shuffle.rs | 9 +- crates/nu-command/src/filters/skip/skip_.rs | 29 +- .../nu-command/src/filters/skip/skip_until.rs | 38 +- .../nu-command/src/filters/skip/skip_while.rs | 39 +- crates/nu-command/src/filters/sort.rs | 8 +- crates/nu-command/src/filters/sort_by.rs | 9 +- crates/nu-command/src/filters/split_by.rs | 10 +- crates/nu-command/src/filters/take/take_.rs | 22 + .../nu-command/src/filters/take/take_until.rs | 38 +- .../nu-command/src/filters/take/take_while.rs | 38 +- crates/nu-command/src/filters/transpose.rs | 14 +- crates/nu-command/src/filters/uniq.rs | 15 +- crates/nu-command/src/filters/update.rs | 3 +- crates/nu-command/src/filters/update_cells.rs | 3 +- crates/nu-command/src/filters/upsert.rs | 11 +- crates/nu-command/src/filters/where_.rs | 29 +- crates/nu-command/src/filters/window.rs | 8 +- crates/nu-command/src/filters/wrap.rs | 5 +- crates/nu-command/src/filters/zip.rs | 47 ++- crates/nu-command/src/formats/from/csv.rs | 19 +- crates/nu-command/src/formats/from/eml.rs | 9 +- crates/nu-command/src/formats/from/ics.rs | 6 +- crates/nu-command/src/formats/from/ini.rs | 11 +- crates/nu-command/src/formats/from/json.rs | 3 +- crates/nu-command/src/formats/from/nuon.rs | 4 +- crates/nu-command/src/formats/from/ods.rs | 4 +- crates/nu-command/src/formats/from/ssv.rs | 3 +- crates/nu-command/src/formats/from/toml.rs | 12 +- crates/nu-command/src/formats/from/tsv.rs | 20 +- crates/nu-command/src/formats/from/url.rs | 12 +- crates/nu-command/src/formats/from/vcf.rs | 6 +- crates/nu-command/src/formats/from/xlsx.rs | 4 +- crates/nu-command/src/formats/from/xml.rs | 10 +- crates/nu-command/src/formats/from/yaml.rs | 6 +- crates/nu-command/src/formats/to/csv.rs | 3 +- crates/nu-command/src/formats/to/html.rs | 3 +- crates/nu-command/src/formats/to/json.rs | 4 +- crates/nu-command/src/formats/to/md.rs | 4 +- crates/nu-command/src/formats/to/nuon.rs | 6 +- crates/nu-command/src/formats/to/text.rs | 6 +- crates/nu-command/src/formats/to/toml.rs | 4 +- crates/nu-command/src/formats/to/tsv.rs | 5 +- crates/nu-command/src/formats/to/url.rs | 6 +- crates/nu-command/src/formats/to/xml.rs | 3 +- crates/nu-command/src/formats/to/yaml.rs | 6 +- crates/nu-command/src/generators/seq.rs | 7 +- crates/nu-command/src/generators/seq_char.rs | 6 +- crates/nu-command/src/generators/seq_date.rs | 3 +- crates/nu-command/src/hash/generic_digest.rs | 6 +- crates/nu-command/src/hash/md5.rs | 6 +- crates/nu-command/src/hash/sha256.rs | 6 +- crates/nu-command/src/math/abs.rs | 11 +- crates/nu-command/src/math/avg.rs | 10 +- crates/nu-command/src/math/ceil.rs | 9 +- crates/nu-command/src/math/eval.rs | 3 +- crates/nu-command/src/math/floor.rs | 9 +- crates/nu-command/src/math/max.rs | 32 +- crates/nu-command/src/math/median.rs | 38 +- crates/nu-command/src/math/min.rs | 30 +- crates/nu-command/src/math/mode.rs | 50 ++- crates/nu-command/src/math/product.rs | 10 +- crates/nu-command/src/math/round.rs | 8 +- crates/nu-command/src/math/sqrt.rs | 11 +- crates/nu-command/src/math/stddev.rs | 15 +- crates/nu-command/src/math/sum.rs | 10 +- crates/nu-command/src/math/variance.rs | 11 +- crates/nu-command/src/network/fetch.rs | 12 +- crates/nu-command/src/network/url/host.rs | 3 +- crates/nu-command/src/network/url/path.rs | 3 +- crates/nu-command/src/network/url/query.rs | 3 +- crates/nu-command/src/network/url/scheme.rs | 3 +- crates/nu-command/src/network/url/url_.rs | 6 +- crates/nu-command/src/path/basename.rs | 7 +- crates/nu-command/src/path/dirname.rs | 3 +- crates/nu-command/src/path/exists.rs | 16 +- crates/nu-command/src/path/expand.rs | 5 +- crates/nu-command/src/path/join.rs | 7 +- crates/nu-command/src/path/parse.rs | 3 +- crates/nu-command/src/path/relative_to.rs | 3 +- crates/nu-command/src/path/split.rs | 18 +- crates/nu-command/src/path/type.rs | 18 +- crates/nu-command/src/platform/ansi/ansi_.rs | 13 +- crates/nu-command/src/platform/ansi/strip.rs | 3 +- crates/nu-command/src/platform/sleep.rs | 28 +- crates/nu-command/src/strings/build_string.rs | 3 +- crates/nu-command/src/strings/char_.rs | 3 +- .../nu-command/src/strings/detect_columns.rs | 3 +- .../src/strings/encode_decode/decode.rs | 3 +- .../strings/encode_decode/decode_base64.rs | 7 +- .../src/strings/encode_decode/encode.rs | 3 +- .../strings/encode_decode/encode_base64.rs | 2 + .../nu-command/src/strings/format/command.rs | 8 +- .../nu-command/src/strings/format/filesize.rs | 11 +- crates/nu-command/src/strings/parse.rs | 3 +- crates/nu-command/src/strings/size.rs | 6 +- crates/nu-command/src/strings/split/chars.rs | 11 +- crates/nu-command/src/strings/split/column.rs | 51 ++- crates/nu-command/src/strings/split/list.rs | 18 +- crates/nu-command/src/strings/split/row.rs | 24 +- crates/nu-command/src/strings/split/words.rs | 4 +- .../src/strings/str_/case/camel_case.rs | 4 +- .../src/strings/str_/case/capitalize.rs | 4 +- .../src/strings/str_/case/downcase.rs | 4 +- .../src/strings/str_/case/kebab_case.rs | 4 +- .../src/strings/str_/case/pascal_case.rs | 4 +- .../strings/str_/case/screaming_snake_case.rs | 4 +- .../src/strings/str_/case/snake_case.rs | 4 +- .../src/strings/str_/case/title_case.rs | 4 +- .../src/strings/str_/case/upcase.rs | 15 +- crates/nu-command/src/strings/str_/collect.rs | 3 +- .../nu-command/src/strings/str_/contains.rs | 4 +- .../nu-command/src/strings/str_/distance.rs | 22 +- .../nu-command/src/strings/str_/ends_with.rs | 6 +- .../nu-command/src/strings/str_/index_of.rs | 4 +- crates/nu-command/src/strings/str_/join.rs | 3 +- crates/nu-command/src/strings/str_/length.rs | 4 +- crates/nu-command/src/strings/str_/lpad.rs | 4 +- crates/nu-command/src/strings/str_/replace.rs | 5 +- crates/nu-command/src/strings/str_/reverse.rs | 4 +- crates/nu-command/src/strings/str_/rpad.rs | 4 +- .../src/strings/str_/starts_with.rs | 4 +- .../nu-command/src/strings/str_/substring.rs | 4 +- .../nu-command/src/strings/str_/trim/trim_.rs | 6 +- crates/nu-command/src/viewers/griddle.rs | 7 +- crates/nu-protocol/Cargo.toml | 2 + crates/nu-protocol/src/signature.rs | 39 ++ crates/nu-protocol/src/syntax_shape.rs | 9 +- crates/nu-protocol/src/ty.rs | 80 +++- crates/nu-protocol/src/value/mod.rs | 70 +++- 238 files changed, 2315 insertions(+), 756 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3953727c7..651c6cb42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2743,6 +2743,8 @@ dependencies = [ "num-format", "serde", "serde_json", + "strum 0.24.1", + "strum_macros 0.24.3", "sys-locale", "thiserror", "typetag", diff --git a/crates/nu-command/src/bits/and.rs b/crates/nu-command/src/bits/and.rs index f7e3a995c..920e3d19a 100644 --- a/crates/nu-command/src/bits/and.rs +++ b/crates/nu-command/src/bits/and.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -15,6 +15,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("bits and") + .input_output_types(vec![(Type::Int, Type::Int)]) + .vectorizes_over_list(true) .required( "target", SyntaxShape::Int, diff --git a/crates/nu-command/src/bits/not.rs b/crates/nu-command/src/bits/not.rs index a6baa27e6..885c887bc 100644 --- a/crates/nu-command/src/bits/not.rs +++ b/crates/nu-command/src/bits/not.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("bits not") + .input_output_types(vec![(Type::Int, Type::Int)]) + .vectorizes_over_list(true) .switch( "signed", "always treat input number as a signed number", diff --git a/crates/nu-command/src/bits/or.rs b/crates/nu-command/src/bits/or.rs index 5c551bf18..9c84c84d9 100644 --- a/crates/nu-command/src/bits/or.rs +++ b/crates/nu-command/src/bits/or.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -15,6 +15,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("bits or") + .input_output_types(vec![(Type::Int, Type::Int)]) + .vectorizes_over_list(true) .required( "target", SyntaxShape::Int, diff --git a/crates/nu-command/src/bits/rotate_left.rs b/crates/nu-command/src/bits/rotate_left.rs index f7a11522e..05163cdce 100644 --- a/crates/nu-command/src/bits/rotate_left.rs +++ b/crates/nu-command/src/bits/rotate_left.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; use num_traits::int::PrimInt; use std::fmt::Display; @@ -18,6 +18,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("bits rol") + .input_output_types(vec![(Type::Int, Type::Int)]) + .vectorizes_over_list(true) .required("bits", SyntaxShape::Int, "number of bits to rotate left") .switch( "signed", diff --git a/crates/nu-command/src/bits/rotate_right.rs b/crates/nu-command/src/bits/rotate_right.rs index 2b5c62fb3..d141f34f6 100644 --- a/crates/nu-command/src/bits/rotate_right.rs +++ b/crates/nu-command/src/bits/rotate_right.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; use num_traits::int::PrimInt; use std::fmt::Display; @@ -18,6 +18,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("bits ror") + .input_output_types(vec![(Type::Int, Type::Int)]) + .vectorizes_over_list(true) .required("bits", SyntaxShape::Int, "number of bits to rotate right") .switch( "signed", diff --git a/crates/nu-command/src/bits/shift_left.rs b/crates/nu-command/src/bits/shift_left.rs index 62c982a87..3c7696235 100644 --- a/crates/nu-command/src/bits/shift_left.rs +++ b/crates/nu-command/src/bits/shift_left.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; use num_traits::CheckedShl; use std::fmt::Display; @@ -18,6 +18,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("bits shl") + .input_output_types(vec![(Type::Int, Type::Int)]) + .vectorizes_over_list(true) .required("bits", SyntaxShape::Int, "number of bits to shift left") .switch( "signed", diff --git a/crates/nu-command/src/bits/shift_right.rs b/crates/nu-command/src/bits/shift_right.rs index 1ffde30a8..fc1cde908 100644 --- a/crates/nu-command/src/bits/shift_right.rs +++ b/crates/nu-command/src/bits/shift_right.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; use num_traits::CheckedShr; use std::fmt::Display; @@ -18,6 +18,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("bits shr") + .input_output_types(vec![(Type::Int, Type::Int)]) + .vectorizes_over_list(true) .required("bits", SyntaxShape::Int, "number of bits to shift right") .switch( "signed", diff --git a/crates/nu-command/src/bits/xor.rs b/crates/nu-command/src/bits/xor.rs index 394ea8a3b..a38cdb939 100644 --- a/crates/nu-command/src/bits/xor.rs +++ b/crates/nu-command/src/bits/xor.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -15,6 +15,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("bits xor") + .input_output_types(vec![(Type::Int, Type::Int)]) + .vectorizes_over_list(true) .required( "target", SyntaxShape::Int, diff --git a/crates/nu-command/src/bytes/add.rs b/crates/nu-command/src/bytes/add.rs index de6f5521b..e7e12d7a5 100644 --- a/crates/nu-command/src/bytes/add.rs +++ b/crates/nu-command/src/bytes/add.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; struct Arguments { added_data: Vec, @@ -30,6 +30,8 @@ impl Command for BytesAdd { fn signature(&self) -> Signature { Signature::build("bytes add") + .input_output_types(vec![(Type::Binary, Type::Binary)]) + .vectorizes_over_list(true) .required("data", SyntaxShape::Binary, "the binary to add") .named( "index", diff --git a/crates/nu-command/src/bytes/at.rs b/crates/nu-command/src/bytes/at.rs index c86eccc67..53b453416 100644 --- a/crates/nu-command/src/bytes/at.rs +++ b/crates/nu-command/src/bytes/at.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use std::cmp::Ordering; @@ -115,6 +115,8 @@ impl Command for BytesAt { fn signature(&self) -> Signature { Signature::build("bytes at") + .input_output_types(vec![(Type::Binary, Type::Binary)]) + .vectorizes_over_list(true) .required("range", SyntaxShape::Any, "the indexes to get bytes") .rest( "rest", diff --git a/crates/nu-command/src/bytes/build_.rs b/crates/nu-command/src/bytes/build_.rs index 4b4f0b623..e4ab0247f 100644 --- a/crates/nu-command/src/bytes/build_.rs +++ b/crates/nu-command/src/bytes/build_.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; #[derive(Clone)] @@ -24,6 +24,7 @@ impl Command for BytesBuild { fn signature(&self) -> nu_protocol::Signature { Signature::build("bytes build") + .input_output_types(vec![(Type::Nothing, Type::Binary)]) .rest("rest", SyntaxShape::Any, "list of bytes") .category(Category::Bytes) } diff --git a/crates/nu-command/src/bytes/collect.rs b/crates/nu-command/src/bytes/collect.rs index 34ddc4470..92173fe4c 100644 --- a/crates/nu-command/src/bytes/collect.rs +++ b/crates/nu-command/src/bytes/collect.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; #[derive(Clone, Copy)] @@ -16,6 +16,7 @@ impl Command for BytesCollect { fn signature(&self) -> Signature { Signature::build("bytes collect") + .input_output_types(vec![(Type::List(Box::new(Type::Binary)), Type::Binary)]) .optional( "separator", SyntaxShape::Binary, diff --git a/crates/nu-command/src/bytes/ends_with.rs b/crates/nu-command/src/bytes/ends_with.rs index 69416f19b..0b41d2e62 100644 --- a/crates/nu-command/src/bytes/ends_with.rs +++ b/crates/nu-command/src/bytes/ends_with.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; struct Arguments { pattern: Vec, @@ -28,6 +28,7 @@ impl Command for BytesEndsWith { fn signature(&self) -> Signature { Signature::build("bytes ends-with") + .input_output_types(vec![(Type::Binary, Type::Bool)]) .required("pattern", SyntaxShape::Binary, "the pattern to match") .rest( "rest", diff --git a/crates/nu-command/src/bytes/index_of.rs b/crates/nu-command/src/bytes/index_of.rs index 52f3765b2..084d755c5 100644 --- a/crates/nu-command/src/bytes/index_of.rs +++ b/crates/nu-command/src/bytes/index_of.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::ast::{Call, CellPath}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; struct Arguments { @@ -29,6 +29,10 @@ impl Command for BytesIndexOf { fn signature(&self) -> Signature { Signature::build("bytes index-of") + .input_output_types(vec![ + (Type::Binary, Type::Int), + (Type::Binary, Type::List(Box::new(Type::Int))), + ]) .required( "pattern", SyntaxShape::Binary, diff --git a/crates/nu-command/src/bytes/length.rs b/crates/nu-command/src/bytes/length.rs index 13f3f70af..3823cbe3b 100644 --- a/crates/nu-command/src/bytes/length.rs +++ b/crates/nu-command/src/bytes/length.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct BytesLen; @@ -16,6 +16,8 @@ impl Command for BytesLen { fn signature(&self) -> Signature { Signature::build("bytes length") + .input_output_types(vec![(Type::Binary, Type::Int)]) + .vectorizes_over_list(true) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/bytes/remove.rs b/crates/nu-command/src/bytes/remove.rs index 42807c6d5..e9106615e 100644 --- a/crates/nu-command/src/bytes/remove.rs +++ b/crates/nu-command/src/bytes/remove.rs @@ -3,7 +3,8 @@ use nu_engine::CallExt; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, + Value, }; struct Arguments { @@ -29,6 +30,7 @@ impl Command for BytesRemove { fn signature(&self) -> Signature { Signature::build("bytes remove") + .input_output_types(vec![(Type::Binary, Type::Binary)]) .required("pattern", SyntaxShape::Binary, "the pattern to find") .rest( "rest", diff --git a/crates/nu-command/src/bytes/replace.rs b/crates/nu-command/src/bytes/replace.rs index c2d8bffb1..124d018b4 100644 --- a/crates/nu-command/src/bytes/replace.rs +++ b/crates/nu-command/src/bytes/replace.rs @@ -3,7 +3,8 @@ use nu_engine::CallExt; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, + Value, }; struct Arguments { @@ -29,6 +30,7 @@ impl Command for BytesReplace { fn signature(&self) -> Signature { Signature::build("bytes replace") + .input_output_types(vec![(Type::Binary, Type::Binary)]) .required("find", SyntaxShape::Binary, "the pattern to find") .required("replace", SyntaxShape::Binary, "the replacement pattern") .rest( diff --git a/crates/nu-command/src/bytes/reverse.rs b/crates/nu-command/src/bytes/reverse.rs index 1ddd583f3..54b945d6f 100644 --- a/crates/nu-command/src/bytes/reverse.rs +++ b/crates/nu-command/src/bytes/reverse.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] @@ -17,6 +17,7 @@ impl Command for BytesReverse { fn signature(&self) -> Signature { Signature::build("bytes reverse") + .input_output_types(vec![(Type::Binary, Type::Binary)]) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/bytes/starts_with.rs b/crates/nu-command/src/bytes/starts_with.rs index 503e4ca06..4242c22f7 100644 --- a/crates/nu-command/src/bytes/starts_with.rs +++ b/crates/nu-command/src/bytes/starts_with.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; struct Arguments { pattern: Vec, @@ -28,6 +28,7 @@ impl Command for BytesStartsWith { fn signature(&self) -> Signature { Signature::build("bytes starts-with") + .input_output_types(vec![(Type::Binary, Type::Bool)]) .required("pattern", SyntaxShape::Binary, "the pattern to match") .rest( "rest", diff --git a/crates/nu-command/src/charting/hashable_value.rs b/crates/nu-command/src/charting/hashable_value.rs index 2a99b2f69..69e68ac35 100644 --- a/crates/nu-command/src/charting/hashable_value.rs +++ b/crates/nu-command/src/charting/hashable_value.rs @@ -13,7 +13,7 @@ use std::hash::{Hash, Hasher}; /// ```text /// assert_eq!(HashableValue::Bool {val: true, span: Span{start: 0, end: 1}}, HashableValue::Bool {val: true, span: Span{start: 90, end: 1000}}) /// ``` -#[derive(Eq, Debug)] +#[derive(Eq, Debug, Ord, PartialOrd)] pub enum HashableValue { Bool { val: bool, diff --git a/crates/nu-command/src/charting/histogram.rs b/crates/nu-command/src/charting/histogram.rs index eba15dae1..88832da9e 100644 --- a/crates/nu-command/src/charting/histogram.rs +++ b/crates/nu-command/src/charting/histogram.rs @@ -1,10 +1,11 @@ use super::hashable_value::HashableValue; +use itertools::Itertools; use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, - Value, + Type, Value, }; use std::collections::HashMap; use std::iter; @@ -24,6 +25,7 @@ impl Command for Histogram { fn signature(&self) -> Signature { Signature::build("histogram") + .input_output_types(vec![(Type::List(Box::new(Type::Any)), Type::Table(vec![])),]) .optional("column-name", SyntaxShape::String, "column name to calc frequency, no need to provide if input is just a list") .optional("frequency-column-name", SyntaxShape::String, "histogram's frequency column, default to be frequency column output") .named("percentage-type", SyntaxShape::String, "percentage calculate method, can be 'normalize' or 'relative', in 'normalize', defaults to be 'normalize'", Some('t')) @@ -36,23 +38,48 @@ impl Command for Histogram { fn examples(&self) -> Vec { vec![ Example { - description: "Get a histogram for the types of files", + description: "Compute a histogram of file types", example: "ls | histogram type", result: None, }, Example { description: - "Get a histogram for the types of files, with frequency column named freq", + "Compute a histogram for the types of files, with frequency column named freq", example: "ls | histogram type freq", result: None, }, Example { - description: "Get a histogram for a list of numbers", - example: "echo [1 2 3 1 1 1 2 2 1 1] | histogram", - result: None, + description: "Compute a histogram for a list of numbers", + example: "echo [1 2 1] | histogram", + result: Some(Value::List { + vals: vec![Value::Record { + cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()], + vals: vec![ + Value::test_int(1), + Value::test_int(2), + Value::test_float(0.6666666666666666), + Value::test_string("66.67%"), + Value::test_string("******************************************************************"), + ], + span: Span::test_data(), + }, + Value::Record { + cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()], + vals: vec![ + Value::test_int(2), + Value::test_int(1), + Value::test_float(0.3333333333333333), + Value::test_string("33.33%"), + Value::test_string("*********************************"), + ], + span: Span::test_data(), + }], + span: Span::test_data(), + } + ), }, Example { - description: "Get a histogram for a list of numbers, and percentage is based on the maximum value", + description: "Compute a histogram for a list of numbers, and percentage is based on the maximum value", example: "echo [1 2 3 1 1 1 2 2 1 1] | histogram --percentage-type relative", result: None, } @@ -213,7 +240,7 @@ fn histogram_impl( freq_column.to_string(), ]; const MAX_FREQ_COUNT: f64 = 100.0; - for (val, count) in counter.into_iter() { + for (val, count) in counter.into_iter().sorted() { let quantile = match calc_method { PercentageCalcMethod::Normalize => count as f64 / total_cnt as f64, PercentageCalcMethod::Relative => count as f64 / max_cnt as f64, diff --git a/crates/nu-command/src/conversions/fmt.rs b/crates/nu-command/src/conversions/fmt.rs index f209b0a1d..832bdd7f9 100644 --- a/crates/nu-command/src/conversions/fmt.rs +++ b/crates/nu-command/src/conversions/fmt.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Type, Value, }; #[derive(Clone)] @@ -19,7 +19,9 @@ impl Command for Fmt { } fn signature(&self) -> nu_protocol::Signature { - Signature::build("fmt").category(Category::Conversions) + Signature::build("fmt") + .input_output_types(vec![(Type::Number, Type::Record(vec![]))]) + .category(Category::Conversions) } fn search_terms(&self) -> Vec<&str> { diff --git a/crates/nu-command/src/conversions/into/binary.rs b/crates/nu-command/src/conversions/into/binary.rs index 0b23968cf..884d5f221 100644 --- a/crates/nu-command/src/conversions/into/binary.rs +++ b/crates/nu-command/src/conversions/into/binary.rs @@ -4,7 +4,7 @@ use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; #[derive(Clone)] @@ -17,6 +17,16 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("into binary") + .input_output_types(vec![ + (Type::Binary, Type::Binary), + (Type::Int, Type::Binary), + (Type::Number, Type::Binary), + (Type::String, Type::Binary), + (Type::Bool, Type::Binary), + (Type::Filesize, Type::Binary), + (Type::Date, Type::Binary), + ]) + .allow_variants_without_examples(true) // TODO: supply exhaustive examples .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/conversions/into/bool.rs b/crates/nu-command/src/conversions/into/bool.rs index 97a37d2f8..b0af12143 100644 --- a/crates/nu-command/src/conversions/into/bool.rs +++ b/crates/nu-command/src/conversions/into/bool.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("into bool") + .input_output_types(vec![ + (Type::Int, Type::Bool), + (Type::Number, Type::Bool), + (Type::String, Type::Bool), + (Type::Bool, Type::Bool), + (Type::List(Box::new(Type::Any)), Type::Table(vec![])), + ]) .rest( "rest", SyntaxShape::CellPath, @@ -89,6 +96,11 @@ impl Command for SubCommand { example: "1 | into bool", result: Some(Value::boolean(true, span)), }, + Example { + description: "convert decimal to boolean", + example: "0.3 | into bool", + result: Some(Value::boolean(true, span)), + }, Example { description: "convert decimal string to boolean", example: "'0.0' | into bool", diff --git a/crates/nu-command/src/conversions/into/datetime.rs b/crates/nu-command/src/conversions/into/datetime.rs index daafbe9c4..9650469ee 100644 --- a/crates/nu-command/src/conversions/into/datetime.rs +++ b/crates/nu-command/src/conversions/into/datetime.rs @@ -7,7 +7,7 @@ use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; struct Arguments { @@ -64,7 +64,11 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("into datetime") - .named( + .input_output_types(vec![ + (Type::Int, Type::Date), + (Type::String, Type::Date), + ]) + .named( "timezone", SyntaxShape::String, "Specify timezone if the input is a Unix timestamp. Valid options: 'UTC' ('u') or 'LOCAL' ('l')", diff --git a/crates/nu-command/src/conversions/into/decimal.rs b/crates/nu-command/src/conversions/into/decimal.rs index a22179ad5..95f240f46 100644 --- a/crates/nu-command/src/conversions/into/decimal.rs +++ b/crates/nu-command/src/conversions/into/decimal.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -15,11 +15,16 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("into decimal").rest( - "rest", - SyntaxShape::CellPath, - "optionally convert text into decimal by column paths", - ) + Signature::build("into decimal") + .input_output_types(vec![ + (Type::String, Type::Number), + (Type::Bool, Type::Number), + ]) + .rest( + "rest", + SyntaxShape::CellPath, + "optionally convert text into decimal by column paths", + ) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/conversions/into/duration.rs b/crates/nu-command/src/conversions/into/duration.rs index b6459e146..9257d796f 100644 --- a/crates/nu-command/src/conversions/into/duration.rs +++ b/crates/nu-command/src/conversions/into/duration.rs @@ -3,7 +3,7 @@ use nu_parser::parse_duration_bytes; use nu_protocol::{ ast::{Call, CellPath, Expr}, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Unit, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Unit, Value, }; @@ -17,6 +17,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("into duration") + .input_output_types(vec![ + (Type::String, Type::Duration), + (Type::Duration, Type::Duration), + // TODO: --convert option should be implemented as `format duration` + (Type::String, Type::String), + (Type::Duration, Type::String), + ]) .named( "convert", SyntaxShape::String, @@ -121,11 +128,19 @@ impl Command for SubCommand { span, }), }, + Example { + description: "Convert duration to duration", + example: "420sec | into duration", + result: Some(Value::Duration { + val: 7 * 60 * 1000 * 1000 * 1000, + span, + }), + }, Example { description: "Convert duration to the requested duration as a string", - example: "420sec | into duration --convert min", + example: "420sec | into duration --convert ms", result: Some(Value::String { - val: "7 min".to_string(), + val: "420000 ms".to_string(), span, }), }, diff --git a/crates/nu-command/src/conversions/into/filesize.rs b/crates/nu-command/src/conversions/into/filesize.rs index 120e2eb1d..545d3e342 100644 --- a/crates/nu-command/src/conversions/into/filesize.rs +++ b/crates/nu-command/src/conversions/into/filesize.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,12 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("into filesize") + .input_output_types(vec![ + (Type::Int, Type::Filesize), + (Type::Number, Type::Filesize), + (Type::String, Type::Filesize), + (Type::Filesize, Type::Filesize), + ]) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/conversions/into/int.rs b/crates/nu-command/src/conversions/into/int.rs index 22a1d873b..2cec85cc5 100644 --- a/crates/nu-command/src/conversions/into/int.rs +++ b/crates/nu-command/src/conversions/into/int.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; struct Arguments { @@ -28,6 +28,16 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("into int") + .input_output_types(vec![ + (Type::String, Type::Int), + (Type::Number, Type::Int), + (Type::Bool, Type::Int), + // Unix timestamp in seconds + (Type::Date, Type::Int), + // TODO: Users should do this by dividing a Filesize by a Filesize explicitly + (Type::Filesize, Type::Int), + ]) + .vectorizes_over_list(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 8eacabfdd..41d0d02de 100644 --- a/crates/nu-command/src/conversions/into/string.rs +++ b/crates/nu-command/src/conversions/into/string.rs @@ -4,7 +4,7 @@ use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, into_code, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, - Span, SyntaxShape, Value, + Span, SyntaxShape, Type, Value, }; use nu_utils::get_system_locale; use num_format::ToFormattedString; @@ -32,6 +32,16 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("into string") + .input_output_types(vec![ + (Type::Binary, Type::String), + (Type::Int, Type::String), + (Type::Number, Type::String), + (Type::String, Type::String), + (Type::Bool, Type::String), + (Type::Filesize, Type::String), + (Type::Date, Type::String), + ]) + .allow_variants_without_examples(true) // https://github.com/nushell/nushell/issues/7032 // FIXME - need to support column paths .rest( "rest", @@ -135,11 +145,12 @@ impl Command for SubCommand { span: Span::test_data(), }), }, - Example { - description: "convert date to string", - example: "date now | into string", - result: None, - }, + // TODO: This should work but does not; see https://github.com/nushell/nushell/issues/7032 + // Example { + // description: "convert date to string", + // example: "'2020-10-10 10:00:00 +02:00' | into datetime | into string", + // result: Some(Value::test_string("Sat Oct 10 10:00:00 2020")), + // }, Example { description: "convert filepath to string", example: "ls Cargo.toml | get name | into string", @@ -147,8 +158,8 @@ impl Command for SubCommand { }, Example { description: "convert filesize to string", - example: "ls Cargo.toml | get size | into string", - result: None, + example: "1KiB | into string", + result: Some(Value::test_string("1,024 B")), }, ] } diff --git a/crates/nu-command/src/core_commands/alias.rs b/crates/nu-command/src/core_commands/alias.rs index 2377c0848..0b81e4359 100644 --- a/crates/nu-command/src/core_commands/alias.rs +++ b/crates/nu-command/src/core_commands/alias.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape}; +use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct Alias; @@ -16,6 +16,7 @@ impl Command for Alias { fn signature(&self) -> nu_protocol::Signature { Signature::build("alias") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("name", SyntaxShape::String, "name of the alias") .required( "initial_value", @@ -53,12 +54,12 @@ impl Command for Alias { Example { description: "Alias ll to ls -l", example: "alias ll = ls -l", - result: None, + result: Some(Value::nothing(Span::test_data())), }, Example { description: "Make an alias that makes a list of all custom commands", example: "alias customs = ($nu.scope.commands | where is_custom | get command)", - result: None, + result: Some(Value::nothing(Span::test_data())), }, ] } diff --git a/crates/nu-command/src/core_commands/ast.rs b/crates/nu-command/src/core_commands/ast.rs index f61719fde..5999018ac 100644 --- a/crates/nu-command/src/core_commands/ast.rs +++ b/crates/nu-command/src/core_commands/ast.rs @@ -3,7 +3,8 @@ use nu_parser::parse; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack, StateWorkingSet}, - Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, + Value, }; #[derive(Clone)] @@ -20,6 +21,7 @@ impl Command for Ast { fn signature(&self) -> Signature { Signature::build("ast") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required( "pipeline", SyntaxShape::String, @@ -50,17 +52,17 @@ impl Command for Ast { Example { description: "Print the ast of a string", example: "ast 'hello'", - result: None, + result: Some(Value::nothing(Span::test_data())), }, Example { description: "Print the ast of a pipeline", example: "ast 'ls | where name =~ README'", - result: None, + result: Some(Value::nothing(Span::test_data())), }, Example { description: "Print the ast of a pipeline with an error", example: "ast 'for x in 1..10 { echo $x '", - result: None, + result: Some(Value::nothing(Span::test_data())), }, ] } diff --git a/crates/nu-command/src/core_commands/commandline.rs b/crates/nu-command/src/core_commands/commandline.rs index 3561d2129..f371613c5 100644 --- a/crates/nu-command/src/core_commands/commandline.rs +++ b/crates/nu-command/src/core_commands/commandline.rs @@ -4,7 +4,7 @@ use nu_protocol::engine::ReplOperation; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; use nu_protocol::IntoPipelineData; -use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Value}; +use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct Commandline; @@ -16,6 +16,7 @@ impl Command for Commandline { fn signature(&self) -> Signature { Signature::build("commandline") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .switch( "append", "appends the string to the end of the buffer", diff --git a/crates/nu-command/src/core_commands/debug.rs b/crates/nu-command/src/core_commands/debug.rs index fd2e67474..8084208ba 100644 --- a/crates/nu-command/src/core_commands/debug.rs +++ b/crates/nu-command/src/core_commands/debug.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct Debug; @@ -15,11 +15,17 @@ impl Command for Debug { } fn signature(&self) -> Signature { - Signature::build("debug").category(Category::Core).switch( - "raw", - "Prints the raw value representation", - Some('r'), - ) + Signature::build("debug") + .input_output_types(vec![ + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::String)), + ), + (Type::Table(vec![]), Type::List(Box::new(Type::String))), + (Type::Any, Type::String), + ]) + .category(Category::Core) + .switch("raw", "Prints the raw value representation", Some('r')) } fn run( @@ -54,12 +60,20 @@ impl Command for Debug { fn examples(&self) -> Vec { vec![ Example { - description: "Print the value of a string", + description: "Debug print a string", example: "'hello' | debug", result: Some(Value::test_string("hello")), }, Example { - description: "Print the value of a table", + description: "Debug print a list", + example: "['hello'] | debug", + result: Some(Value::List { + vals: vec![Value::test_string("hello")], + span: Span::test_data(), + }), + }, + Example { + description: "Debug print a table", example: "echo [[version patch]; [0.1.0 false] [0.1.1 true] [0.2.0 false]] | debug", result: Some(Value::List { vals: vec![ diff --git a/crates/nu-command/src/core_commands/def.rs b/crates/nu-command/src/core_commands/def.rs index 19f2928a0..f812efe0d 100644 --- a/crates/nu-command/src/core_commands/def.rs +++ b/crates/nu-command/src/core_commands/def.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Value}; +use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct Def; @@ -16,6 +16,7 @@ impl Command for Def { fn signature(&self) -> nu_protocol::Signature { Signature::build("def") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("def_name", SyntaxShape::String, "definition name") .required("params", SyntaxShape::Signature, "parameters") .required( diff --git a/crates/nu-command/src/core_commands/def_env.rs b/crates/nu-command/src/core_commands/def_env.rs index 1115a715f..4cc0a668f 100644 --- a/crates/nu-command/src/core_commands/def_env.rs +++ b/crates/nu-command/src/core_commands/def_env.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct DefEnv; @@ -16,6 +16,7 @@ impl Command for DefEnv { fn signature(&self) -> nu_protocol::Signature { Signature::build("def-env") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("def_name", SyntaxShape::String, "definition name") .required("params", SyntaxShape::Signature, "parameters") .required( diff --git a/crates/nu-command/src/core_commands/describe.rs b/crates/nu-command/src/core_commands/describe.rs index 88b7a1dff..e44268ee9 100644 --- a/crates/nu-command/src/core_commands/describe.rs +++ b/crates/nu-command/src/core_commands/describe.rs @@ -1,7 +1,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Value, + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value, }; #[derive(Clone)] @@ -17,7 +17,9 @@ impl Command for Describe { } fn signature(&self) -> Signature { - Signature::build("describe").category(Category::Core) + Signature::build("describe") + .input_output_types(vec![(Type::Any, Type::String)]) + .category(Category::Core) } fn run( diff --git a/crates/nu-command/src/core_commands/do_.rs b/crates/nu-command/src/core_commands/do_.rs index 5fdd9afc1..69efb4d6c 100644 --- a/crates/nu-command/src/core_commands/do_.rs +++ b/crates/nu-command/src/core_commands/do_.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, ListStream, PipelineData, RawStream, ShellError, Signature, SyntaxShape, - Value, + Type, Value, }; #[derive(Clone)] @@ -20,6 +20,7 @@ impl Command for Do { fn signature(&self) -> nu_protocol::Signature { Signature::build("do") + .input_output_types(vec![(Type::Any, Type::Any)]) .required("block", SyntaxShape::Any, "the block to run") .switch( "ignore-errors", @@ -197,8 +198,13 @@ impl Command for Do { }, Example { description: "Run the block, with a positional parameter", - example: r#"do {|x| 100 + $x } 50"#, - result: Some(Value::test_int(150)), + example: r#"do {|x| 100 + $x } 77"#, + result: Some(Value::test_int(177)), + }, + Example { + description: "Run the block, with input", + example: r#"77 | do {|x| 100 + $in }"#, + result: None, // TODO: returns 177 }, ] } diff --git a/crates/nu-command/src/core_commands/echo.rs b/crates/nu-command/src/core_commands/echo.rs index c5d2c38e5..21f6d266e 100644 --- a/crates/nu-command/src/core_commands/echo.rs +++ b/crates/nu-command/src/core_commands/echo.rs @@ -2,7 +2,8 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, ListStream, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, ListStream, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, + Value, }; #[derive(Clone)] @@ -19,6 +20,7 @@ impl Command for Echo { fn signature(&self) -> Signature { Signature::build("echo") + .input_output_types(vec![(Type::Nothing, Type::Any)]) .rest("rest", SyntaxShape::Any, "the values to echo") .category(Category::Core) } diff --git a/crates/nu-command/src/core_commands/export.rs b/crates/nu-command/src/core_commands/export.rs index 6dc7c5f74..f5e2c40e0 100644 --- a/crates/nu-command/src/core_commands/export.rs +++ b/crates/nu-command/src/core_commands/export.rs @@ -2,7 +2,7 @@ use nu_engine::get_full_help; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Example, IntoPipelineData, PipelineData, Signature, Span, Value, + Category, Example, IntoPipelineData, PipelineData, Signature, Span, Type, Value, }; #[derive(Clone)] @@ -14,7 +14,9 @@ impl Command for ExportCommand { } fn signature(&self) -> Signature { - Signature::build("export").category(Category::Core) + Signature::build("export") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) + .category(Category::Core) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/core_commands/export_alias.rs b/crates/nu-command/src/core_commands/export_alias.rs index 9f3106493..04f3cba42 100644 --- a/crates/nu-command/src/core_commands/export_alias.rs +++ b/crates/nu-command/src/core_commands/export_alias.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape}; +use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type}; #[derive(Clone)] pub struct ExportAlias; @@ -16,6 +16,7 @@ impl Command for ExportAlias { fn signature(&self) -> nu_protocol::Signature { Signature::build("export alias") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("name", SyntaxShape::String, "name of the alias") .required( "initial_value", diff --git a/crates/nu-command/src/core_commands/export_def.rs b/crates/nu-command/src/core_commands/export_def.rs index 1ab327100..dce5c81d7 100644 --- a/crates/nu-command/src/core_commands/export_def.rs +++ b/crates/nu-command/src/core_commands/export_def.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct ExportDef; @@ -16,6 +16,7 @@ impl Command for ExportDef { fn signature(&self) -> nu_protocol::Signature { Signature::build("export def") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("name", SyntaxShape::String, "definition name") .required("params", SyntaxShape::Signature, "parameters") .required( diff --git a/crates/nu-command/src/core_commands/export_def_env.rs b/crates/nu-command/src/core_commands/export_def_env.rs index 4ea55cd1e..15ad86cdc 100644 --- a/crates/nu-command/src/core_commands/export_def_env.rs +++ b/crates/nu-command/src/core_commands/export_def_env.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct ExportDefEnv; @@ -16,6 +16,7 @@ impl Command for ExportDefEnv { fn signature(&self) -> nu_protocol::Signature { Signature::build("export def-env") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("name", SyntaxShape::String, "definition name") .required("params", SyntaxShape::Signature, "parameters") .required( diff --git a/crates/nu-command/src/core_commands/export_extern.rs b/crates/nu-command/src/core_commands/export_extern.rs index b718a80af..cbca9ed40 100644 --- a/crates/nu-command/src/core_commands/export_extern.rs +++ b/crates/nu-command/src/core_commands/export_extern.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape}; +use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type}; #[derive(Clone)] pub struct ExportExtern; @@ -16,6 +16,7 @@ impl Command for ExportExtern { fn signature(&self) -> nu_protocol::Signature { Signature::build("export extern") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("def_name", SyntaxShape::String, "definition name") .required("params", SyntaxShape::Signature, "parameters") .category(Category::Core) diff --git a/crates/nu-command/src/core_commands/export_use.rs b/crates/nu-command/src/core_commands/export_use.rs index 48676a647..61b26aafa 100644 --- a/crates/nu-command/src/core_commands/export_use.rs +++ b/crates/nu-command/src/core_commands/export_use.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct ExportUse; @@ -16,6 +16,7 @@ impl Command for ExportUse { fn signature(&self) -> nu_protocol::Signature { Signature::build("export use") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("pattern", SyntaxShape::ImportPattern, "import pattern") .category(Category::Core) } diff --git a/crates/nu-command/src/core_commands/extern_.rs b/crates/nu-command/src/core_commands/extern_.rs index 51a89bb62..1bef269b2 100644 --- a/crates/nu-command/src/core_commands/extern_.rs +++ b/crates/nu-command/src/core_commands/extern_.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape}; +use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type}; #[derive(Clone)] pub struct Extern; @@ -16,6 +16,7 @@ impl Command for Extern { fn signature(&self) -> nu_protocol::Signature { Signature::build("extern") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("def_name", SyntaxShape::String, "definition name") .required("params", SyntaxShape::Signature, "parameters") .category(Category::Core) diff --git a/crates/nu-command/src/core_commands/for_.rs b/crates/nu-command/src/core_commands/for_.rs index 58d4394a5..bf807dd5a 100644 --- a/crates/nu-command/src/core_commands/for_.rs +++ b/crates/nu-command/src/core_commands/for_.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, ListStream, PipelineData, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -20,6 +20,7 @@ impl Command for For { fn signature(&self) -> nu_protocol::Signature { Signature::build("for") + .input_output_types(vec![(Type::Nothing, Type::List(Box::new(Type::Any)))]) .required( "var_name", SyntaxShape::VarWithOptType, diff --git a/crates/nu-command/src/core_commands/help.rs b/crates/nu-command/src/core_commands/help.rs index 11d47ec4b..c98dc44c1 100644 --- a/crates/nu-command/src/core_commands/help.rs +++ b/crates/nu-command/src/core_commands/help.rs @@ -1,4 +1,5 @@ use fancy_regex::Regex; +use itertools::Itertools; use nu_ansi_term::{ Color::{Default, Red, White}, Style, @@ -9,7 +10,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, - ShellError, Signature, Span, Spanned, SyntaxShape, Value, + ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; use std::borrow::Borrow; #[derive(Clone)] @@ -22,6 +23,7 @@ impl Command for Help { fn signature(&self) -> Signature { Signature::build("help") + .input_output_types(vec![(Type::Nothing, Type::String)]) .rest( "rest", SyntaxShape::String, @@ -162,6 +164,16 @@ fn help( span: head, }); + cols.push("signatures".into()); + vals.push(Value::String { + val: sig + .input_output_types + .iter() + .map(|(i, o)| format!("{:?} => {:?}", i.to_shape(), o.to_shape())) + .join("\n"), + span: head, + }); + cols.push("search_terms".into()); vals.push(if search_terms.is_empty() { Value::nothing(head) @@ -259,6 +271,16 @@ fn help( span: head, }); + cols.push("signatures".into()); + vals.push(Value::String { + val: sig + .input_output_types + .iter() + .map(|(i, o)| format!("{:?} => {:?}", i.to_shape(), o.to_shape())) + .join("\n"), + span: head, + }); + cols.push("search_terms".into()); vals.push(if search_terms.is_empty() { Value::nothing(head) diff --git a/crates/nu-command/src/core_commands/hide.rs b/crates/nu-command/src/core_commands/hide.rs index 096da23cb..a79858953 100644 --- a/crates/nu-command/src/core_commands/hide.rs +++ b/crates/nu-command/src/core_commands/hide.rs @@ -1,7 +1,7 @@ use nu_protocol::ast::{Call, Expr, Expression}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -14,6 +14,7 @@ impl Command for Hide { fn signature(&self) -> nu_protocol::Signature { Signature::build("hide") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("pattern", SyntaxShape::ImportPattern, "import pattern") .category(Category::Core) } diff --git a/crates/nu-command/src/core_commands/hide_env.rs b/crates/nu-command/src/core_commands/hide_env.rs index 3bee3d77c..f446b5f1d 100644 --- a/crates/nu-command/src/core_commands/hide_env.rs +++ b/crates/nu-command/src/core_commands/hide_env.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ did_you_mean, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,7 @@ impl Command for HideEnv { fn signature(&self) -> nu_protocol::Signature { Signature::build("hide-env") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .rest( "name", SyntaxShape::String, diff --git a/crates/nu-command/src/core_commands/if_.rs b/crates/nu-command/src/core_commands/if_.rs index 4821c54d4..ebf975646 100644 --- a/crates/nu-command/src/core_commands/if_.rs +++ b/crates/nu-command/src/core_commands/if_.rs @@ -2,7 +2,7 @@ use nu_engine::{eval_block, eval_expression, eval_expression_with_input, CallExt use nu_protocol::ast::Call; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, FromValue, PipelineData, ShellError, Signature, SyntaxShape, Value, + Category, Example, FromValue, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -19,6 +19,7 @@ impl Command for If { fn signature(&self) -> nu_protocol::Signature { Signature::build("if") + .input_output_types(vec![(Type::Any, Type::Any)]) .required("cond", SyntaxShape::Expression, "condition to check") .required( "then_block", diff --git a/crates/nu-command/src/core_commands/ignore.rs b/crates/nu-command/src/core_commands/ignore.rs index 682506f40..610978d15 100644 --- a/crates/nu-command/src/core_commands/ignore.rs +++ b/crates/nu-command/src/core_commands/ignore.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature}; +use nu_protocol::{Category, Example, PipelineData, Signature, Span, Type, Value}; #[derive(Clone)] pub struct Ignore; @@ -15,7 +15,9 @@ impl Command for Ignore { } fn signature(&self) -> nu_protocol::Signature { - Signature::build("ignore").category(Category::Core) + Signature::build("ignore") + .input_output_types(vec![(Type::Any, Type::Nothing)]) + .category(Category::Core) } fn search_terms(&self) -> Vec<&str> { @@ -37,7 +39,7 @@ impl Command for Ignore { vec![Example { description: "Ignore the output of an echo command", example: "echo done | ignore", - result: None, + result: Some(Value::nothing(Span::test_data())), }] } } diff --git a/crates/nu-command/src/core_commands/let_.rs b/crates/nu-command/src/core_commands/let_.rs index 3b750b994..23dcb7011 100644 --- a/crates/nu-command/src/core_commands/let_.rs +++ b/crates/nu-command/src/core_commands/let_.rs @@ -1,7 +1,7 @@ use nu_engine::eval_expression_with_input; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape}; +use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type}; #[derive(Clone)] pub struct Let; @@ -17,6 +17,8 @@ impl Command for Let { fn signature(&self) -> nu_protocol::Signature { Signature::build("let") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) + .allow_variants_without_examples(true) .required("var_name", SyntaxShape::VarWithOptType, "variable name") .required( "initial_value", diff --git a/crates/nu-command/src/core_commands/metadata.rs b/crates/nu-command/src/core_commands/metadata.rs index 3697236b9..04fcd2d46 100644 --- a/crates/nu-command/src/core_commands/metadata.rs +++ b/crates/nu-command/src/core_commands/metadata.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, Expr, Expression}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, DataSource, Example, IntoPipelineData, PipelineData, PipelineMetadata, Signature, - Span, SyntaxShape, Value, + Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -20,6 +20,8 @@ impl Command for Metadata { fn signature(&self) -> nu_protocol::Signature { Signature::build("metadata") + .input_output_types(vec![(Type::Nothing, Type::Record(vec![]))]) + .allow_variants_without_examples(true) .optional( "expression", SyntaxShape::Any, diff --git a/crates/nu-command/src/core_commands/module.rs b/crates/nu-command/src/core_commands/module.rs index 93edb68b9..c01dc30d1 100644 --- a/crates/nu-command/src/core_commands/module.rs +++ b/crates/nu-command/src/core_commands/module.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct Module; @@ -16,6 +16,7 @@ impl Command for Module { fn signature(&self) -> nu_protocol::Signature { Signature::build("module") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("module_name", SyntaxShape::String, "module name") .required( "block", diff --git a/crates/nu-command/src/core_commands/register.rs b/crates/nu-command/src/core_commands/register.rs index d175480b5..eaf6ee342 100644 --- a/crates/nu-command/src/core_commands/register.rs +++ b/crates/nu-command/src/core_commands/register.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape}; +use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type}; #[derive(Clone)] pub struct Register; @@ -16,6 +16,7 @@ impl Command for Register { fn signature(&self) -> nu_protocol::Signature { Signature::build("register") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required( "plugin", SyntaxShape::Filepath, diff --git a/crates/nu-command/src/core_commands/use_.rs b/crates/nu-command/src/core_commands/use_.rs index 02155f233..73a13d290 100644 --- a/crates/nu-command/src/core_commands/use_.rs +++ b/crates/nu-command/src/core_commands/use_.rs @@ -2,7 +2,7 @@ use nu_engine::{eval_block, find_in_dirs_env, redirect_env}; use nu_protocol::ast::{Call, Expr, Expression}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -19,6 +19,7 @@ impl Command for Use { fn signature(&self) -> nu_protocol::Signature { Signature::build("use") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("pattern", SyntaxShape::ImportPattern, "import pattern") .category(Category::Core) } diff --git a/crates/nu-command/src/core_commands/version.rs b/crates/nu-command/src/core_commands/version.rs index b01a683c8..d6f314873 100644 --- a/crates/nu-command/src/core_commands/version.rs +++ b/crates/nu-command/src/core_commands/version.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Example, IntoPipelineData, PipelineData, ShellError, Signature, Value}; +use nu_protocol::{Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value}; pub mod shadow { include!(concat!(env!("OUT_DIR"), "/shadow.rs")); @@ -16,6 +16,8 @@ impl Command for Version { fn signature(&self) -> Signature { Signature::build("version") + .input_output_types(vec![(Type::Nothing, Type::Record(vec![]))]) + .allow_variants_without_examples(true) } fn usage(&self) -> &str { @@ -215,3 +217,13 @@ fn features_enabled() -> Vec { names } + +#[cfg(test)] +mod test { + #[test] + fn test_examples() { + use super::Version; + use crate::test_examples; + test_examples(Version {}) + } +} diff --git a/crates/nu-command/src/date/format.rs b/crates/nu-command/src/date/format.rs index c7032f9f7..55a76b742 100644 --- a/crates/nu-command/src/date/format.rs +++ b/crates/nu-command/src/date/format.rs @@ -4,7 +4,8 @@ use nu_engine::CallExt; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, + Value, }; use nu_utils::locale::get_system_locale_string; use std::fmt::{Display, Write}; @@ -21,6 +22,11 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("date format") + .input_output_types(vec![ + (Type::Date, Type::String), + (Type::String, Type::String), + ]) + .allow_variants_without_examples(true) // https://github.com/nushell/nushell/issues/7032 .switch("list", "lists strftime cheatsheet", Some('l')) .optional( "format string", @@ -66,8 +72,18 @@ impl Command for SubCommand { fn examples(&self) -> Vec { vec![ + // TODO: This should work but does not; see https://github.com/nushell/nushell/issues/7032 + // Example { + // description: "Format a given date-time using the default format (RFC 2822).", + // example: r#"'2021-10-22 20:00:12 +01:00' | into datetime | date format"#, + // result: Some(Value::String { + // val: "Fri, 22 Oct 2021 20:00:12 +0100".to_string(), + // span: Span::test_data(), + // }), + // }, Example { - description: "Format a given date using the default format (RFC 2822).", + description: + "Format a given date-time as a string using the default format (RFC 2822).", example: r#""2021-10-22 20:00:12 +01:00" | date format"#, result: Some(Value::String { val: "Fri, 22 Oct 2021 20:00:12 +0100".to_string(), @@ -75,13 +91,13 @@ impl Command for SubCommand { }), }, Example { - description: "Format a given date using a given format string.", - example: "date format '%Y-%m-%d'", + description: "Format the current date-time using a given format string.", + example: r#"date now | date format "%Y-%m-%d %H:%M:%S""#, result: None, }, Example { - description: "Format a given date using a given format string.", - example: r#"date format "%Y-%m-%d %H:%M:%S""#, + description: "Format the current date using a given format string.", + example: r#"date now | date format "%Y-%m-%d %H:%M:%S""#, result: None, }, Example { diff --git a/crates/nu-command/src/date/humanize.rs b/crates/nu-command/src/date/humanize.rs index 7b070a733..91b0cab7a 100644 --- a/crates/nu-command/src/date/humanize.rs +++ b/crates/nu-command/src/date/humanize.rs @@ -3,7 +3,7 @@ use chrono::{DateTime, FixedOffset, Local}; use chrono_humanize::HumanTime; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -13,7 +13,13 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("date humanize").category(Category::Date) + Signature::build("date humanize") + .input_output_types(vec![ + (Type::Date, Type::String), + (Type::String, Type::String), + ]) + .allow_variants_without_examples(true) + .category(Category::Date) } fn usage(&self) -> &str { @@ -45,21 +51,11 @@ impl Command for SubCommand { } fn examples(&self) -> Vec { - vec![ - Example { - description: "Print a 'humanized' format for the date, relative to now.", - example: "date humanize", - result: Some(Value::String { - val: "now".to_string(), - span: Span::test_data(), - }), - }, - Example { - description: "Print a 'humanized' format for the date, relative to now.", - example: r#""2021-10-22 20:00:12 +01:00" | date humanize"#, - result: None, - }, - ] + vec![Example { + description: "Print a 'humanized' format for the date, relative to now.", + example: r#""2021-10-22 20:00:12 +01:00" | date humanize"#, + result: None, + }] } } diff --git a/crates/nu-command/src/date/list_timezone.rs b/crates/nu-command/src/date/list_timezone.rs index f2827d351..3945e1ceb 100644 --- a/crates/nu-command/src/date/list_timezone.rs +++ b/crates/nu-command/src/date/list_timezone.rs @@ -2,7 +2,7 @@ use chrono_tz::TZ_VARIANTS; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoInterruptiblePipelineData, PipelineData, Signature, Span, Value, + Category, Example, IntoInterruptiblePipelineData, PipelineData, Signature, Span, Type, Value, }; #[derive(Clone)] @@ -14,7 +14,9 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("date list-timezone").category(Category::Date) + Signature::build("date list-timezone") + .input_output_types(vec![(Type::Nothing, Type::Table(vec![]))]) + .category(Category::Date) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/date/now.rs b/crates/nu-command/src/date/now.rs index fd2d8f4ec..7dd6a8be3 100644 --- a/crates/nu-command/src/date/now.rs +++ b/crates/nu-command/src/date/now.rs @@ -1,7 +1,7 @@ use chrono::Local; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, IntoPipelineData, PipelineData, Signature, Value}; +use nu_protocol::{Category, Example, IntoPipelineData, PipelineData, Signature, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -11,7 +11,9 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("date now").category(Category::Date) + Signature::build("date now") + .input_output_types(vec![(Type::Nothing, Type::Date)]) + .category(Category::Date) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/date/to_record.rs b/crates/nu-command/src/date/to_record.rs index 44633943f..96ae33805 100644 --- a/crates/nu-command/src/date/to_record.rs +++ b/crates/nu-command/src/date/to_record.rs @@ -2,6 +2,7 @@ use crate::date::utils::parse_date_from_string; use chrono::{DateTime, Datelike, FixedOffset, Local, Timelike}; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::Type; use nu_protocol::{ Category, Example, PipelineData, ShellError::DatetimeParseError, Signature, Span, Value, }; @@ -15,11 +16,17 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("date to-record").category(Category::Date) + Signature::build("date to-record") + .input_output_types(vec![ + (Type::Date, Type::Record(vec![])), + (Type::String, Type::Record(vec![])), + ]) + .allow_variants_without_examples(true) // https://github.com/nushell/nushell/issues/7032 + .category(Category::Date) } fn usage(&self) -> &str { - "Convert the date into a structured table." + "Convert the date into a record." } fn search_terms(&self) -> Vec<&str> { @@ -38,46 +45,54 @@ impl Command for SubCommand { } fn examples(&self) -> Vec { + let example_result_1 = || { + let span = Span::test_data(); + let cols = vec![ + "year".into(), + "month".into(), + "day".into(), + "hour".into(), + "minute".into(), + "second".into(), + "timezone".into(), + ]; + let vals = vec![ + Value::Int { val: 2020, span }, + Value::Int { val: 4, span }, + Value::Int { val: 12, span }, + Value::Int { val: 22, span }, + Value::Int { val: 10, span }, + Value::Int { val: 57, span }, + Value::String { + val: "+02:00".to_string(), + span, + }, + ]; + Some(Value::Record { cols, vals, span }) + }; + vec![ Example { - description: "Convert the current date into a structured table.", - example: "date to-table", + description: "Convert the current date into a record.", + example: "date to-record", result: None, }, Example { - description: "Convert the current date into a structured table.", + description: "Convert the current date into a record.", example: "date now | date to-record", result: None, }, Example { - description: "Convert a given date into a structured table.", - example: " '2020-04-12 22:10:57 +0200' | date to-record", - result: { - let span = Span::test_data(); - let cols = vec![ - "year".into(), - "month".into(), - "day".into(), - "hour".into(), - "minute".into(), - "second".into(), - "timezone".into(), - ]; - let vals = vec![ - Value::Int { val: 2020, span }, - Value::Int { val: 4, span }, - Value::Int { val: 12, span }, - Value::Int { val: 22, span }, - Value::Int { val: 10, span }, - Value::Int { val: 57, span }, - Value::String { - val: "+02:00".to_string(), - span, - }, - ]; - Some(Value::Record { cols, vals, span }) - }, + description: "Convert a date string into a record.", + example: "'2020-04-12 22:10:57 +0200' | date to-record", + result: example_result_1(), }, + // TODO: This should work but does not; see https://github.com/nushell/nushell/issues/7032 + // Example { + // description: "Convert a date into a record.", + // example: "'2020-04-12 22:10:57 +0200' | into datetime | date to-record", + // result: example_result_1(), + // }, ] } } diff --git a/crates/nu-command/src/date/to_table.rs b/crates/nu-command/src/date/to_table.rs index 4de85ac6e..d71020953 100644 --- a/crates/nu-command/src/date/to_table.rs +++ b/crates/nu-command/src/date/to_table.rs @@ -2,6 +2,7 @@ use crate::date::utils::parse_date_from_string; use chrono::{DateTime, Datelike, FixedOffset, Local, Timelike}; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::Type; use nu_protocol::{ Category, Example, PipelineData, ShellError::DatetimeParseError, Signature, Span, Value, }; @@ -15,7 +16,13 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("date to-table").category(Category::Date) + Signature::build("date to-table") + .input_output_types(vec![ + (Type::Date, Type::Table(vec![])), + (Type::String, Type::Table(vec![])), + ]) + .allow_variants_without_examples(true) // https://github.com/nushell/nushell/issues/7032 + .category(Category::Date) } fn usage(&self) -> &str { @@ -38,49 +45,57 @@ impl Command for SubCommand { } fn examples(&self) -> Vec { + let example_result_1 = || { + let span = Span::test_data(); + let cols = vec![ + "year".into(), + "month".into(), + "day".into(), + "hour".into(), + "minute".into(), + "second".into(), + "timezone".into(), + ]; + let vals = vec![ + Value::Int { val: 2020, span }, + Value::Int { val: 4, span }, + Value::Int { val: 12, span }, + Value::Int { val: 22, span }, + Value::Int { val: 10, span }, + Value::Int { val: 57, span }, + Value::String { + val: "+02:00".to_string(), + span, + }, + ]; + Some(Value::List { + vals: vec![Value::Record { cols, vals, span }], + span, + }) + }; + vec![ Example { - description: "Convert the date into a structured table.", + description: "Convert the current date into a table.", example: "date to-table", result: None, }, Example { - description: "Convert the date into a structured table.", + description: "Convert the date into a table.", example: "date now | date to-table", result: None, }, Example { - description: "Convert a given date into a structured table.", - example: " '2020-04-12 22:10:57 +0200' | date to-table", - result: { - let span = Span::test_data(); - let cols = vec![ - "year".into(), - "month".into(), - "day".into(), - "hour".into(), - "minute".into(), - "second".into(), - "timezone".into(), - ]; - let vals = vec![ - Value::Int { val: 2020, span }, - Value::Int { val: 4, span }, - Value::Int { val: 12, span }, - Value::Int { val: 22, span }, - Value::Int { val: 10, span }, - Value::Int { val: 57, span }, - Value::String { - val: "+02:00".to_string(), - span, - }, - ]; - Some(Value::List { - vals: vec![Value::Record { cols, vals, span }], - span, - }) - }, + description: "Convert a given date into a table.", + example: "'2020-04-12 22:10:57 +0200' | date to-table", + result: example_result_1(), }, + // TODO: This should work but does not; see https://github.com/nushell/nushell/issues/7032 + // Example { + // description: "Convert a given date into a table.", + // example: "'2020-04-12 22:10:57 +0200' | into datetime | date to-table", + // result: example_result_1(), + // }, ] } } diff --git a/crates/nu-command/src/date/to_timezone.rs b/crates/nu-command/src/date/to_timezone.rs index 9c690f622..7a33c301e 100644 --- a/crates/nu-command/src/date/to_timezone.rs +++ b/crates/nu-command/src/date/to_timezone.rs @@ -5,7 +5,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; use chrono::{FixedOffset, TimeZone}; @@ -20,6 +20,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("date to-timezone") + .input_output_types(vec![(Type::Date, Type::Date), (Type::String, Type::Date)]) + .allow_variants_without_examples(true) // https://github.com/nushell/nushell/issues/7032 .required("time zone", SyntaxShape::String, "time zone description") .category(Category::Date) } @@ -62,6 +64,16 @@ impl Command for SubCommand { } fn examples(&self) -> Vec { + let example_result_1 = || { + let dt = FixedOffset::east(5 * 3600) + .ymd(2020, 10, 10) + .and_hms(13, 00, 00); + Some(Value::Date { + val: dt, + span: Span::test_data(), + }) + }; + vec![ Example { description: "Get the current date in UTC+05:00", @@ -81,19 +93,14 @@ impl Command for SubCommand { Example { description: "Get the current date in Hawaii", example: r#""2020-10-10 10:00:00 +02:00" | date to-timezone "+0500""#, - // result: None - // The following should be the result of the test, but it fails. Cannot figure it out why. - result: { - let dt = FixedOffset::east(5 * 3600) - .ymd(2020, 10, 10) - .and_hms(13, 00, 00); - - Some(Value::Date { - val: dt, - span: Span::test_data(), - }) - }, + result: example_result_1(), }, + // TODO: This should work but does not; see https://github.com/nushell/nushell/issues/7032 + // Example { + // description: "Get the current date in Hawaii, from a datetime object", + // example: r#""2020-10-10 10:00:00 +02:00" | into datetime | date to-timezone "+0500""#, + // result: example_result_1(), + // }, ] } } diff --git a/crates/nu-command/src/env/export_env.rs b/crates/nu-command/src/env/export_env.rs index 60635013d..88003495b 100644 --- a/crates/nu-command/src/env/export_env.rs +++ b/crates/nu-command/src/env/export_env.rs @@ -2,7 +2,7 @@ use nu_engine::{eval_block, redirect_env, CallExt}; use nu_protocol::{ ast::Call, engine::{CaptureBlock, Command, EngineState, Stack}, - Category, Example, PipelineData, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -15,6 +15,7 @@ impl Command for ExportEnv { fn signature(&self) -> Signature { Signature::build("export-env") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required( "block", SyntaxShape::Block(Some(vec![])), @@ -53,11 +54,20 @@ impl Command for ExportEnv { } fn examples(&self) -> Vec { - vec![Example { - description: "Set an environment", - example: r#"export-env { let-env SPAM = 'eggs' }; $env.SPAM"#, - result: Some(Value::string("eggs", Span::test_data())), - }] + vec![ + Example { + description: "Set an environment variable", + example: r#"export-env { let-env SPAM = 'eggs' }"#, + result: Some(Value::Nothing { + span: Span::test_data(), + }), + }, + Example { + description: "Set an environment variable and examine its value", + example: r#"export-env { let-env SPAM = 'eggs' }; $env.SPAM"#, + result: Some(Value::string("eggs", Span::test_data())), + }, + ] } } diff --git a/crates/nu-command/src/env/with_env.rs b/crates/nu-command/src/env/with_env.rs index 817b71a21..a1f5b86e9 100644 --- a/crates/nu-command/src/env/with_env.rs +++ b/crates/nu-command/src/env/with_env.rs @@ -4,7 +4,7 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::{ ast::Call, engine::{CaptureBlock, Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -17,6 +17,7 @@ impl Command for WithEnv { fn signature(&self) -> Signature { Signature::build("with-env") + .input_output_types(vec![(Type::Any, Type::Any)]) .required( "variable", SyntaxShape::Any, diff --git a/crates/nu-command/src/example_test.rs b/crates/nu-command/src/example_test.rs index 018026295..549afd5a0 100644 --- a/crates/nu-command/src/example_test.rs +++ b/crates/nu-command/src/example_test.rs @@ -1,75 +1,206 @@ #[cfg(test)] -use nu_engine::eval_block; -#[cfg(test)] -use nu_parser::parse; -#[cfg(test)] -use nu_protocol::{ - engine::{Command, EngineState, Stack, StateWorkingSet}, - PipelineData, Span, Value, -}; - -#[cfg(test)] -use crate::To; - -#[cfg(test)] -use super::{ - Ansi, Date, From, If, Into, LetEnv, Math, Path, Random, Split, SplitColumn, SplitRow, Str, - StrJoin, StrLength, StrReplace, Url, Wrap, -}; +use nu_protocol::engine::Command; #[cfg(test)] pub fn test_examples(cmd: impl Command + 'static) { - use crate::BuildString; + test_examples::test_examples(cmd); +} - let examples = cmd.examples(); - let mut engine_state = Box::new(EngineState::new()); - - let delta = { - // Base functions that are needed for testing - // Try to keep this working set small to keep tests running as fast as possible - let mut working_set = StateWorkingSet::new(&*engine_state); - working_set.add_decl(Box::new(Str)); - working_set.add_decl(Box::new(StrJoin)); - working_set.add_decl(Box::new(StrLength)); - working_set.add_decl(Box::new(StrReplace)); - working_set.add_decl(Box::new(BuildString)); - working_set.add_decl(Box::new(From)); - working_set.add_decl(Box::new(If)); - working_set.add_decl(Box::new(To)); - working_set.add_decl(Box::new(Into)); - working_set.add_decl(Box::new(Random)); - working_set.add_decl(Box::new(Split)); - working_set.add_decl(Box::new(SplitColumn)); - working_set.add_decl(Box::new(SplitRow)); - working_set.add_decl(Box::new(Math)); - working_set.add_decl(Box::new(Path)); - working_set.add_decl(Box::new(Date)); - working_set.add_decl(Box::new(Url)); - working_set.add_decl(Box::new(Ansi)); - working_set.add_decl(Box::new(Wrap)); - working_set.add_decl(Box::new(LetEnv)); - - use super::Echo; - working_set.add_decl(Box::new(Echo)); - // Adding the command that is being tested to the working set - working_set.add_decl(Box::new(cmd)); - - working_set.render() +#[cfg(test)] +mod test_examples { + use super::super::{ + Ansi, BuildString, Date, Echo, From, If, Into, LetEnv, Math, Path, Random, Split, + SplitColumn, SplitRow, Str, StrJoin, StrLength, StrReplace, Url, Wrap, }; + use crate::To; + use itertools::Itertools; + use nu_engine; + use nu_parser; + use nu_protocol::{ + ast::Block, + engine::{Command, EngineState, Stack, StateDelta, StateWorkingSet}, + Example, PipelineData, Signature, Span, Type, Value, + }; + use std::{collections::HashSet, path::PathBuf}; - let cwd = std::env::current_dir().expect("Could not get current working directory."); + pub fn test_examples(cmd: impl Command + 'static) { + let examples = cmd.examples(); + let signature = cmd.signature(); + let mut engine_state = make_engine_state(cmd.clone_box()); - engine_state - .merge_delta(delta) - .expect("Error merging delta"); + let cwd = std::env::current_dir().expect("Could not get current working directory."); - for example in examples { - // Skip tests that don't have results to compare to - if example.result.is_none() { - continue; + let mut witnessed_type_transformations = HashSet::<(Type, Type)>::new(); + + for example in examples { + if example.result.is_none() { + continue; + } + witnessed_type_transformations.extend( + check_example_input_and_output_types_match_command_signature( + &example, + &cwd, + &mut make_engine_state(cmd.clone_box()), + &signature.input_output_types, + signature.operates_on_cell_paths(), + signature.vectorizes_over_list, + ), + ); + check_example_evaluates_to_expected_output(&example, &cwd, &mut engine_state); } - let start = std::time::Instant::now(); + check_all_signature_input_output_types_entries_have_examples( + signature, + witnessed_type_transformations, + ); + } + + fn make_engine_state(cmd: Box) -> Box { + let mut engine_state = Box::new(EngineState::new()); + + let delta = { + // Base functions that are needed for testing + // Try to keep this working set small to keep tests running as fast as possible + let mut working_set = StateWorkingSet::new(&*engine_state); + working_set.add_decl(Box::new(Str)); + working_set.add_decl(Box::new(StrJoin)); + working_set.add_decl(Box::new(StrLength)); + working_set.add_decl(Box::new(StrReplace)); + working_set.add_decl(Box::new(BuildString)); + working_set.add_decl(Box::new(From)); + working_set.add_decl(Box::new(If)); + working_set.add_decl(Box::new(To)); + working_set.add_decl(Box::new(Into)); + working_set.add_decl(Box::new(Random)); + working_set.add_decl(Box::new(Split)); + working_set.add_decl(Box::new(SplitColumn)); + working_set.add_decl(Box::new(SplitRow)); + working_set.add_decl(Box::new(Math)); + working_set.add_decl(Box::new(Path)); + working_set.add_decl(Box::new(Date)); + working_set.add_decl(Box::new(Url)); + working_set.add_decl(Box::new(Ansi)); + working_set.add_decl(Box::new(Wrap)); + working_set.add_decl(Box::new(LetEnv)); + working_set.add_decl(Box::new(Echo)); + // Adding the command that is being tested to the working set + working_set.add_decl(cmd); + + working_set.render() + }; + + engine_state + .merge_delta(delta) + .expect("Error merging delta"); + engine_state + } + + fn check_example_input_and_output_types_match_command_signature<'a>( + example: &Example, + cwd: &PathBuf, + engine_state: &mut Box, + signature_input_output_types: &Vec<(Type, Type)>, + signature_operates_on_cell_paths: bool, + signature_vectorizes_over_list: bool, + ) -> HashSet<(Type, Type)> { + let mut witnessed_type_transformations = HashSet::<(Type, Type)>::new(); + + // Skip tests that don't have results to compare to + if let Some(example_output) = example.result.as_ref() { + if let Some(example_input_type) = + eval_pipeline_without_terminal_expression(example.example, &cwd, engine_state) + { + let example_input_type = example_input_type.get_type(); + let example_output_type = example_output.get_type(); + + let example_matches_signature = + signature_input_output_types + .iter() + .any(|(sig_in_type, sig_out_type)| { + example_input_type.is_subtype(sig_in_type) + && example_output_type.is_subtype(sig_out_type) + && { + witnessed_type_transformations + .insert((sig_in_type.clone(), sig_out_type.clone())); + true + } + }); + + // The example type checks as vectorization over an input list if both: + // 1. The command is declared to vectorize over list input. + // 2. There exists an entry t -> u in the type map such that the + // example_input_type is a subtype of list and the + // example_output_type is a subtype of list. + let example_matches_signature_via_vectorization_over_list = + signature_vectorizes_over_list + && match &example_input_type { + Type::List(ex_in_type) => { + match signature_input_output_types.iter().find_map( + |(sig_in_type, sig_out_type)| { + if ex_in_type.is_subtype(sig_in_type) { + Some((sig_in_type, sig_out_type)) + } else { + None + } + }, + ) { + Some((sig_in_type, sig_out_type)) => match &example_output_type + { + Type::List(ex_out_type) + if ex_out_type.is_subtype(sig_out_type) => + { + witnessed_type_transformations.insert(( + sig_in_type.clone(), + sig_out_type.clone(), + )); + true + } + _ => false, + }, + None => false, + } + } + _ => false, + }; + + // The example type checks as a cell path operation if both: + // 1. The command is declared to operate on cell paths. + // 2. The example_input_type is list or record or table, and the example + // output shape is the same as the input shape. + let example_matches_signature_via_cell_path_operation = + signature_operates_on_cell_paths + && example_input_type.accepts_cell_paths() + // TODO: This is too permissive; it should make use of the signature.input_output_types at least. + && example_output_type.to_shape() == example_input_type.to_shape(); + + if !(example_matches_signature + || example_matches_signature_via_vectorization_over_list + || example_matches_signature_via_cell_path_operation) + { + assert!( + false, + "The example `{}` demonstrates a transformation of type {:?} -> {:?}. \ + However, this does not match the declared signature: {:?}.{} \ + For this command, `vectorizes_over_list` is {} and `operates_on_cell_paths()` is {}.", + example.example, + example_input_type, + example_output_type, + signature_input_output_types, + if signature_input_output_types.is_empty() { " (Did you forget to declare the input and output types for the command?)" } else { "" }, + signature_vectorizes_over_list, + signature_operates_on_cell_paths + ); + }; + }; + } + witnessed_type_transformations + } + + fn check_example_evaluates_to_expected_output( + example: &Example, + cwd: &PathBuf, + engine_state: &mut Box, + ) { let mut stack = Stack::new(); // Set up PWD @@ -85,30 +216,82 @@ pub fn test_examples(cmd: impl Command + 'static) { .merge_env(&mut stack, &cwd) .expect("Error merging environment"); - let (block, delta) = { - let mut working_set = StateWorkingSet::new(&*engine_state); - let (output, err) = parse( - &mut working_set, - None, - example.example.as_bytes(), - false, - &[], + let empty_input = PipelineData::new(Span::test_data()); + let result = eval(example.example, empty_input, &cwd, engine_state); + + // Note. Value implements PartialEq for Bool, Int, Float, String and Block + // If the command you are testing requires to compare another case, then + // you need to define its equality in the Value struct + if let Some(expected) = example.result.as_ref() { + assert_eq!( + &result, expected, + "The example result differs from the expected value", + ) + } + } + + fn check_all_signature_input_output_types_entries_have_examples( + signature: Signature, + witnessed_type_transformations: HashSet<(Type, Type)>, + ) { + let declared_type_transformations = + HashSet::from_iter(signature.input_output_types.into_iter()); + assert!( + witnessed_type_transformations.is_subset(&declared_type_transformations), + "This should not be possible (bug in test): the type transformations \ + collected in the course of matching examples to the signature type map \ + contain type transformations not present in the signature type map." + ); + + if !signature.allow_variants_without_examples { + assert_eq!( + witnessed_type_transformations, + declared_type_transformations, + "There are entries in the signature type map which do not correspond to any example: \ + {:?}", + declared_type_transformations + .difference(&witnessed_type_transformations) + .map(|(s1, s2)| format!("{} -> {}", s1, s2)) + .join(", ") ); + } + } - if let Some(err) = err { - panic!("test parse error in `{}`: {:?}", example.example, err) - } + fn eval( + contents: &str, + input: PipelineData, + cwd: &PathBuf, + engine_state: &mut Box, + ) -> Value { + let (block, delta) = parse(contents, engine_state); + eval_block(block, input, cwd, engine_state, delta) + } - (output, working_set.render()) - }; + fn parse(contents: &str, engine_state: &Box) -> (Block, StateDelta) { + let mut working_set = StateWorkingSet::new(&*engine_state); + let (output, err) = + nu_parser::parse(&mut working_set, None, contents.as_bytes(), false, &[]); + if let Some(err) = err { + panic!("test parse error in `{}`: {:?}", contents, err) + } + + (output, working_set.render()) + } + + fn eval_block( + block: Block, + input: PipelineData, + cwd: &PathBuf, + engine_state: &mut Box, + delta: StateDelta, + ) -> Value { engine_state .merge_delta(delta) .expect("Error merging delta"); let mut stack = Stack::new(); - // Set up PWD stack.add_env_var( "PWD".to_string(), Value::String { @@ -117,33 +300,34 @@ pub fn test_examples(cmd: impl Command + 'static) { }, ); - match eval_block( - &engine_state, - &mut stack, - &block, - PipelineData::new(Span::test_data()), - true, - true, - ) { - Err(err) => panic!("test eval error in `{}`: {:?}", example.example, err), - Ok(result) => { - let result = result.into_value(Span::test_data()); - println!("input: {}", example.example); - println!("result: {:?}", result); - println!("done: {:?}", start.elapsed()); + match nu_engine::eval_block(&engine_state, &mut stack, &block, input, true, true) { + Err(err) => panic!("test eval error in `{}`: {:?}", "TODO", err), + Ok(result) => result.into_value(Span::test_data()), + } + } - // Note. Value implements PartialEq for Bool, Int, Float, String and Block - // If the command you are testing requires to compare another case, then - // you need to define its equality in the Value struct - if let Some(expected) = example.result { - if result != expected { - panic!( - "the example result is different to expected value: {:?} != {:?}", - result, expected - ) - } + fn eval_pipeline_without_terminal_expression( + src: &str, + cwd: &PathBuf, + engine_state: &mut Box, + ) -> Option { + let (mut block, delta) = parse(src, engine_state); + match block.pipelines.len() { + 1 => { + let n_expressions = block.pipelines[0].expressions.len(); + block.pipelines[0].expressions.truncate(&n_expressions - 1); + + if !block.pipelines[0].expressions.is_empty() { + let empty_input = PipelineData::new(Span::test_data()); + Some(eval_block(block, empty_input, &cwd, engine_state, delta)) + } else { + Some(Value::nothing(Span::test_data())) } } + _ => { + // E.g. multiple semicolon-separated statements + None + } } } } diff --git a/crates/nu-command/src/filters/all.rs b/crates/nu-command/src/filters/all.rs index e81c9f938..f8f2ae618 100644 --- a/crates/nu-command/src/filters/all.rs +++ b/crates/nu-command/src/filters/all.rs @@ -2,7 +2,8 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::{ ast::Call, engine::{CaptureBlock, Command, EngineState, Stack}, - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value, + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type, + Value, }; #[derive(Clone)] @@ -15,6 +16,10 @@ impl Command for All { fn signature(&self) -> Signature { Signature::build(self.name()) + .input_output_types(vec![ + (Type::List(Box::new(Type::Any)), Type::Bool), + (Type::Table(vec![]), Type::Bool), + ]) .required( "predicate", SyntaxShape::RowCondition, diff --git a/crates/nu-command/src/filters/any.rs b/crates/nu-command/src/filters/any.rs index e8de50cff..335c6597a 100644 --- a/crates/nu-command/src/filters/any.rs +++ b/crates/nu-command/src/filters/any.rs @@ -2,7 +2,8 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::{ ast::Call, engine::{CaptureBlock, Command, EngineState, Stack}, - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value, + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type, + Value, }; #[derive(Clone)] @@ -15,6 +16,10 @@ impl Command for Any { fn signature(&self) -> Signature { Signature::build(self.name()) + .input_output_types(vec![ + (Type::List(Box::new(Type::Any)), Type::Bool), + (Type::Table(vec![]), Type::Bool), + ]) .required( "predicate", SyntaxShape::RowCondition, diff --git a/crates/nu-command/src/filters/append.rs b/crates/nu-command/src/filters/append.rs index bacbfdf50..6592f67bb 100644 --- a/crates/nu-command/src/filters/append.rs +++ b/crates/nu-command/src/filters/append.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,10 @@ 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)), + )]) .required("row", SyntaxShape::Any, "the row, list, or table to append") .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/collect.rs b/crates/nu-command/src/filters/collect.rs index bd78a34db..f994fed71 100644 --- a/crates/nu-command/src/filters/collect.rs +++ b/crates/nu-command/src/filters/collect.rs @@ -2,7 +2,7 @@ use nu_engine::{eval_block, redirect_env, CallExt}; use nu_protocol::ast::Call; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, Signature, SyntaxShape, Value, + Category, Example, IntoPipelineData, PipelineData, Signature, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -15,6 +15,7 @@ impl Command for Collect { fn signature(&self) -> Signature { Signature::build("collect") + .input_output_types(vec![(Type::List(Box::new(Type::Any)), Type::Any)]) .required( "block", SyntaxShape::Block(Some(vec![SyntaxShape::Any])), diff --git a/crates/nu-command/src/filters/columns.rs b/crates/nu-command/src/filters/columns.rs index 840b1e055..2b96bbd16 100644 --- a/crates/nu-command/src/filters/columns.rs +++ b/crates/nu-command/src/filters/columns.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, Value, + Signature, Span, Type, Value, }; #[derive(Clone)] @@ -15,7 +15,12 @@ impl Command for Columns { } fn signature(&self) -> Signature { - Signature::build(self.name()).category(Category::Filters) + Signature::build(self.name()) + .input_output_types(vec![( + Type::Table(vec![]), + Type::List(Box::new(Type::String)), + )]) + .category(Category::Filters) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/filters/compact.rs b/crates/nu-command/src/filters/compact.rs index 066841b96..979c41879 100644 --- a/crates/nu-command/src/filters/compact.rs +++ b/crates/nu-command/src/filters/compact.rs @@ -1,7 +1,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::Call, engine::Command, engine::EngineState, engine::Stack, Category, Example, - PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -14,6 +14,19 @@ impl Command for Compact { fn signature(&self) -> Signature { Signature::build("compact") + .input_output_types(vec![ + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + (Type::Table(vec![]), Type::Table(vec![])), + ( + // TODO: Should table be a subtype of List? If so then this + // entry would be unnecessary. + Type::Table(vec![]), + Type::List(Box::new(Type::Any)), + ), + ]) .rest( "columns", SyntaxShape::Any, diff --git a/crates/nu-command/src/filters/default.rs b/crates/nu-command/src/filters/default.rs index cf02fbc23..1d24f8815 100644 --- a/crates/nu-command/src/filters/default.rs +++ b/crates/nu-command/src/filters/default.rs @@ -1,7 +1,9 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, Span, Spanned, SyntaxShape, Value}; +use nu_protocol::{ + Category, Example, PipelineData, Signature, Span, Spanned, SyntaxShape, Type, Value, +}; #[derive(Clone)] pub struct Default; @@ -13,6 +15,9 @@ impl Command for Default { fn signature(&self) -> Signature { Signature::build("default") + // TODO: Give more specific type signature? + // TODO: Declare usage of cell paths in signature? (It seems to behave as if it uses cell paths) + .input_output_types(vec![(Type::Any, Type::Any)]) .required( "default value", SyntaxShape::Any, diff --git a/crates/nu-command/src/filters/drop/column.rs b/crates/nu-command/src/filters/drop/column.rs index 68db65579..0aa312966 100644 --- a/crates/nu-command/src/filters/drop/column.rs +++ b/crates/nu-command/src/filters/drop/column.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, - ShellError, Signature, Span, SyntaxShape, Value, + ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,7 @@ impl Command for DropColumn { fn signature(&self) -> Signature { Signature::build(self.name()) + .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) .optional( "columns", SyntaxShape::Int, @@ -25,7 +26,7 @@ impl Command for DropColumn { } fn usage(&self) -> &str { - "Remove the last number of columns. If you want to remove columns by name, try 'reject'." + "Remove N columns at the right-hand end of the input table. To remove columns by name, use 'reject'." } fn search_terms(&self) -> Vec<&str> { diff --git a/crates/nu-command/src/filters/drop/drop_.rs b/crates/nu-command/src/filters/drop/drop_.rs index 863f8b7db..ce2bb0258 100644 --- a/crates/nu-command/src/filters/drop/drop_.rs +++ b/crates/nu-command/src/filters/drop/drop_.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -17,16 +17,19 @@ impl Command for Drop { fn signature(&self) -> Signature { Signature::build("drop") - .optional( - "rows", - SyntaxShape::Int, - "starting from the back, the number of rows to remove", - ) + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ]) + .optional("rows", SyntaxShape::Int, "The number of items to remove") .category(Category::Filters) } fn usage(&self) -> &str { - "Remove the last several rows of the input. Counterpart of 'skip'. Opposite of 'last'." + "Remove items/rows from the end of the input list/table. Counterpart of 'skip'. Opposite of 'last'." } fn search_terms(&self) -> Vec<&str> { @@ -37,7 +40,7 @@ impl Command for Drop { vec![ Example { example: "[0,1,2,3] | drop", - description: "Remove the last item of a list/table", + description: "Remove the last item of a list", result: Some(Value::List { vals: vec![Value::test_int(0), Value::test_int(1), Value::test_int(2)], span: Span::test_data(), @@ -45,7 +48,7 @@ impl Command for Drop { }, Example { example: "[0,1,2,3] | drop 0", - description: "Remove zero item of a list/table", + description: "Remove zero item of a list", result: Some(Value::List { vals: vec![ Value::test_int(0), @@ -58,12 +61,24 @@ impl Command for Drop { }, Example { example: "[0,1,2,3] | drop 2", - description: "Remove the last two items of a list/table", + description: "Remove the last two items of a list", result: Some(Value::List { vals: vec![Value::test_int(0), Value::test_int(1)], span: Span::test_data(), }), }, + Example { + description: "Remove the last row in a table", + example: "[[a, b]; [1, 2] [3, 4]] | drop 1", + result: Some(Value::List { + vals: vec![Value::Record { + cols: vec!["a".to_string(), "b".to_string()], + vals: vec![Value::test_int(1), Value::test_int(2)], + span: Span::test_data(), + }], + span: Span::test_data(), + }), + }, ] } diff --git a/crates/nu-command/src/filters/drop/nth.rs b/crates/nu-command/src/filters/drop/nth.rs index e3c59b391..cab013540 100644 --- a/crates/nu-command/src/filters/drop/nth.rs +++ b/crates/nu-command/src/filters/drop/nth.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::{Call, RangeInclusion}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, FromValue, IntoInterruptiblePipelineData, PipelineData, PipelineIterator, - Range, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Range, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -17,6 +17,10 @@ impl Command for DropNth { fn signature(&self) -> Signature { Signature::build("drop nth") + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + )]) .required( "row number or row range", // FIXME: we can make this accept either Int or Range when we can compose SyntaxShapes diff --git a/crates/nu-command/src/filters/each.rs b/crates/nu-command/src/filters/each.rs index 42bc53f5e..f591b4ffa 100644 --- a/crates/nu-command/src/filters/each.rs +++ b/crates/nu-command/src/filters/each.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, - Span, SyntaxShape, Value, + Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -35,6 +35,10 @@ with 'transpose' first."# fn signature(&self) -> nu_protocol::Signature { Signature::build("each") + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + )]) .required( "block", SyntaxShape::Block(Some(vec![SyntaxShape::Any])), diff --git a/crates/nu-command/src/filters/each_while.rs b/crates/nu-command/src/filters/each_while.rs index f0459cff2..70b7e29cd 100644 --- a/crates/nu-command/src/filters/each_while.rs +++ b/crates/nu-command/src/filters/each_while.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, - Span, SyntaxShape, Value, + Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -24,6 +24,10 @@ impl Command for EachWhile { fn signature(&self) -> nu_protocol::Signature { Signature::build(self.name()) + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + )]) .required( "block", SyntaxShape::Block(Some(vec![SyntaxShape::Any])), diff --git a/crates/nu-command/src/filters/empty.rs b/crates/nu-command/src/filters/empty.rs index bb4b96efe..3a8840aef 100644 --- a/crates/nu-command/src/filters/empty.rs +++ b/crates/nu-command/src/filters/empty.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::ast::{Call, CellPath}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Value, + Category, Example, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -15,6 +15,7 @@ impl Command for Empty { fn signature(&self) -> Signature { Signature::build("is-empty") + .input_output_types(vec![(Type::Any, Type::Bool)]) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/filters/every.rs b/crates/nu-command/src/filters/every.rs index 71cf5462c..5ddd7919b 100644 --- a/crates/nu-command/src/filters/every.rs +++ b/crates/nu-command/src/filters/every.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -17,6 +17,10 @@ impl Command for Every { fn signature(&self) -> Signature { Signature::build("every") + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + )]) .required( "stride", SyntaxShape::Int, diff --git a/crates/nu-command/src/filters/find.rs b/crates/nu-command/src/filters/find.rs index a2385b578..80595e56a 100644 --- a/crates/nu-command/src/filters/find.rs +++ b/crates/nu-command/src/filters/find.rs @@ -9,7 +9,7 @@ use nu_protocol::{ ast::Call, engine::{CaptureBlock, Command, EngineState, Stack}, Category, Config, Example, IntoInterruptiblePipelineData, ListStream, PipelineData, ShellError, - Signature, Span, SyntaxShape, Value, + Signature, Span, SyntaxShape, Type, Value, }; use nu_utils::get_ls_colors; @@ -23,6 +23,20 @@ impl Command for Find { fn signature(&self) -> Signature { Signature::build(self.name()) + .input_output_types(vec![ + ( + // TODO: This is too permissive; if we could express this + // using a type parameter it would be List -> List. + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + (Type::String, Type::String), + ( + // For find -p + Type::Table(vec![]), + Type::Table(vec![]), + ), + ]) .named( "predicate", SyntaxShape::Block(Some(vec![SyntaxShape::Any])), diff --git a/crates/nu-command/src/filters/first.rs b/crates/nu-command/src/filters/first.rs index fec2f1f58..80430ea1d 100644 --- a/crates/nu-command/src/filters/first.rs +++ b/crates/nu-command/src/filters/first.rs @@ -16,6 +16,24 @@ impl Command for First { fn signature(&self) -> Signature { Signature::build("first") + .input_output_types(vec![ + ( + // TODO: This variant duplicates the functionality of + // `take`. See #6611, #6611, #6893 + // TODO: This is too permissive; if we could express this + // using a type parameter style it would be List -> + // List. + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ( + // TODO: This is too permissive; if we could express this + // using a type parameter it would be List -> T. + Type::List(Box::new(Type::Any)), + Type::Any, + ), + (Type::Binary, Type::Binary), + ]) .optional( "rows", SyntaxShape::Int, diff --git a/crates/nu-command/src/filters/flatten.rs b/crates/nu-command/src/filters/flatten.rs index cdbd9ccd3..a4d8d9e84 100644 --- a/crates/nu-command/src/filters/flatten.rs +++ b/crates/nu-command/src/filters/flatten.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -17,6 +17,13 @@ impl Command for Flatten { fn signature(&self) -> Signature { Signature::build("flatten") + .input_output_types(vec![ + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + (Type::Record(vec![]), Type::Table(vec![])), + ]) .rest( "rest", SyntaxShape::String, diff --git a/crates/nu-command/src/filters/get.rs b/crates/nu-command/src/filters/get.rs index 42bd3f29e..f9b8e9a9e 100644 --- a/crates/nu-command/src/filters/get.rs +++ b/crates/nu-command/src/filters/get.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, - SyntaxShape, Value, + Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -20,6 +20,15 @@ impl Command for Get { fn signature(&self) -> nu_protocol::Signature { Signature::build("get") + .input_output_types(vec![ + ( + // TODO: This is too permissive; if we could express this + // using a type parameter it would be List -> T. + Type::List(Box::new(Type::Any)), + Type::Any, + ), + (Type::Table(vec![]), Type::Any), + ]) .required( "cell_path", SyntaxShape::CellPath, @@ -92,6 +101,24 @@ impl Command for Get { } fn examples(&self) -> Vec { vec![ + Example { + description: "Get an item from a list", + example: "[0 1 2] | get 1", + result: Some(Value::test_int(1)), + }, + Example { + description: "Get a column from a table", + example: "[{A: A0}] | get A", + result: Some(Value::List { + vals: vec![Value::test_string("A0")], + span: Span::test_data(), + }), + }, + Example { + description: "Get a cell from a table", + example: "[{A: A0}] | get 0.A", + result: Some(Value::test_string("A0")), + }, Example { description: "Extract the name of files as a list", example: "ls | get name", @@ -125,3 +152,15 @@ impl Command for Get { ] } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(Get) + } +} diff --git a/crates/nu-command/src/filters/group.rs b/crates/nu-command/src/filters/group.rs index 414d26ffe..62869b073 100644 --- a/crates/nu-command/src/filters/group.rs +++ b/crates/nu-command/src/filters/group.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, PipelineData, Signature, Span, Spanned, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,14 @@ impl Command for Group { fn signature(&self) -> Signature { Signature::build("group") + // TODO: It accepts Table also, but currently there is no Table + // example. Perhaps Table should be a subtype of List, in which case + // the current signature would suffice even when a Table example + // exists. + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::List(Box::new(Type::Any)))), + )]) .required("group_size", SyntaxShape::Int, "the size of each group") .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/group_by.rs b/crates/nu-command/src/filters/group_by.rs index 7e938329e..49d2364a0 100644 --- a/crates/nu-command/src/filters/group_by.rs +++ b/crates/nu-command/src/filters/group_by.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, - Value, + Type, Value, }; use indexmap::IndexMap; @@ -17,15 +17,20 @@ impl Command for GroupBy { } fn signature(&self) -> Signature { - Signature::build("group-by").optional( - "grouper", - SyntaxShape::Any, - "the grouper value to use", - ) + Signature::build("group-by") + // TODO: It accepts Table also, but currently there is no Table + // example. Perhaps Table should be a subtype of List, in which case + // the current signature would suffice even when a Table example + // exists. + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::Record(vec![]), + )]) + .optional("grouper", SyntaxShape::Any, "the grouper value to use") } fn usage(&self) -> &str { - "Split a table into groups based on one column's values, and return a record with those groups." + "Splits a list or table into groups, and returns a record containing those groups." } fn run( @@ -47,7 +52,7 @@ impl Command for GroupBy { }, Example { description: "You can also group by raw values by leaving out the argument", - example: "echo ['1' '3' '1' '3' '2' '1' '1'] | group-by", + example: "['1' '3' '1' '3' '2' '1' '1'] | group-by", result: Some(Value::Record { cols: vec!["1".to_string(), "3".to_string(), "2".to_string()], vals: vec![ diff --git a/crates/nu-command/src/filters/headers.rs b/crates/nu-command/src/filters/headers.rs index aeb9f61d9..50b318402 100644 --- a/crates/nu-command/src/filters/headers.rs +++ b/crates/nu-command/src/filters/headers.rs @@ -1,7 +1,8 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value, + Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, + Value, }; #[derive(Clone)] @@ -13,7 +14,16 @@ impl Command for Headers { } fn signature(&self) -> Signature { - Signature::build(self.name()).category(Category::Filters) + Signature::build(self.name()) + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + ( + // Tables with missing values are List + Type::List(Box::new(Type::Any)), + Type::Table(vec![]), + ), + ]) + .category(Category::Filters) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/filters/insert.rs b/crates/nu-command/src/filters/insert.rs index b3c1c46c4..8682d5186 100644 --- a/crates/nu-command/src/filters/insert.rs +++ b/crates/nu-command/src/filters/insert.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember}; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, - ShellError, Signature, Span, SyntaxShape, Value, + ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,14 @@ impl Command for Insert { fn signature(&self) -> Signature { Signature::build("insert") + .input_output_types(vec![ + (Type::Record(vec![]), Type::Record(vec![])), + // TODO: It accepts table input also (in which case it repeats + // the value across all table rows) but currently there is no + // example of the table variant so it cannot be in the + // signature. + // (Type::Table(vec![]), Type::Table(vec![])), + ]) .required( "field", SyntaxShape::CellPath, @@ -49,8 +57,8 @@ impl Command for Insert { fn examples(&self) -> Vec { vec![Example { - description: "Insert a new value", - example: "echo {'name': 'nu', 'stars': 5} | insert alias 'Nushell'", + description: "Insert a new entry into a record", + example: "{'name': 'nu', 'stars': 5} | insert alias 'Nushell'", result: Some(Value::Record { cols: vec!["name".into(), "stars".into(), "alias".into()], vals: vec![ diff --git a/crates/nu-command/src/filters/last.rs b/crates/nu-command/src/filters/last.rs index 09e55ce3f..f96076931 100644 --- a/crates/nu-command/src/filters/last.rs +++ b/crates/nu-command/src/filters/last.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, SyntaxShape, Value, + Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -17,6 +17,23 @@ impl Command for Last { fn signature(&self) -> Signature { Signature::build("last") + .input_output_types(vec![ + ( + // TODO: This variant duplicates the functionality of + // `take`. See #6611, #6611, #6893 + // TODO: This is too permissive; if we could express this + // using a type parameter style it would be List -> + // List. + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ( + // TODO: This is too permissive; if we could express this + // using a type parameter it would be List -> T. + Type::List(Box::new(Type::Any)), + Type::Any, + ), + ]) .optional( "rows", SyntaxShape::Int, diff --git a/crates/nu-command/src/filters/length.rs b/crates/nu-command/src/filters/length.rs index eb41b145f..9ad4e6282 100644 --- a/crates/nu-command/src/filters/length.rs +++ b/crates/nu-command/src/filters/length.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, Value, + Signature, Span, Type, Value, }; #[derive(Clone)] @@ -20,6 +20,10 @@ impl Command for Length { fn signature(&self) -> nu_protocol::Signature { Signature::build("length") + .input_output_types(vec![ + (Type::List(Box::new(Type::Any)), Type::Int), + (Type::Table(vec![]), Type::Int), + ]) .switch("column", "Show the number of columns in a table", Some('c')) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/lines.rs b/crates/nu-command/src/filters/lines.rs index dcc47806a..02b4e8c6c 100644 --- a/crates/nu-command/src/filters/lines.rs +++ b/crates/nu-command/src/filters/lines.rs @@ -2,7 +2,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, PipelineData, RawStream, ShellError, - Signature, Span, Value, + Signature, Span, Type, Value, }; #[derive(Clone)] @@ -19,6 +19,7 @@ impl Command for Lines { fn signature(&self) -> nu_protocol::Signature { Signature::build("lines") + .input_output_types(vec![(Type::String, Type::List(Box::new(Type::String)))]) .switch("skip-empty", "skip empty lines", Some('s')) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/merge.rs b/crates/nu-command/src/filters/merge.rs index 2a9f306f2..b047d5e48 100644 --- a/crates/nu-command/src/filters/merge.rs +++ b/crates/nu-command/src/filters/merge.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, - ShellError, Signature, Span, SyntaxShape, Value, + ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -29,6 +29,10 @@ repeating this process with row 1, and so on."# fn signature(&self) -> nu_protocol::Signature { Signature::build("merge") + .input_output_types(vec![ + (Type::Record(vec![]), Type::Record(vec![])), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .required( "block", // Both this and `update` should have a shape more like | | than just . -Leon 2022-10-27 @@ -64,10 +68,11 @@ repeating this process with row 1, and so on."# Example { example: "{a: 1, b: 2} | merge {c: 3}", description: "Merge two records", - result: Some(Value::test_record( - vec!["a", "b", "c"], - vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)], - )), + result: Some(Value::Record { + cols: vec!["a".to_string(), "b".to_string(), "c".to_string()], + vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)], + span: Span::test_data(), + }), }, Example { example: "{a: 1, b: 3} | merge { { b: 2 } | merge { c: 4 } }", @@ -77,6 +82,17 @@ repeating this process with row 1, and so on."# vec![Value::test_int(1), Value::test_int(2), Value::test_int(4)], )), }, + Example { + example: "[{columnA: A0 columnB: B0}] | merge [{columnA: 'A0*'}]", + description: "Merge two tables, overwriting overlapping columns", + result: Some(Value::List { + vals: vec![Value::test_record( + vec!["columnA", "columnB"], + vec![Value::test_string("A0*"), Value::test_string("B0")], + )], + span: Span::test_data(), + }), + }, ] } diff --git a/crates/nu-command/src/filters/move_.rs b/crates/nu-command/src/filters/move_.rs index 25490ee5e..52eb6a579 100644 --- a/crates/nu-command/src/filters/move_.rs +++ b/crates/nu-command/src/filters/move_.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, Spanned, SyntaxShape, Value, + Signature, Span, Spanned, SyntaxShape, Type, Value, }; #[derive(Clone, Debug)] @@ -26,6 +26,10 @@ impl Command for Move { fn signature(&self) -> nu_protocol::Signature { Signature::build("move") + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + (Type::Record(vec![]), Type::Record(vec![])), + ]) .rest("columns", SyntaxShape::String, "the columns to move") .named( "after", diff --git a/crates/nu-command/src/filters/par_each.rs b/crates/nu-command/src/filters/par_each.rs index d1d257275..fc1f4ea73 100644 --- a/crates/nu-command/src/filters/par_each.rs +++ b/crates/nu-command/src/filters/par_each.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, - Span, SyntaxShape, Value, + Span, SyntaxShape, Type, Value, }; use rayon::prelude::*; @@ -23,6 +23,10 @@ impl Command for ParEach { fn signature(&self) -> nu_protocol::Signature { Signature::build("par-each") + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + )]) .required( "block", SyntaxShape::Block(Some(vec![SyntaxShape::Any])), diff --git a/crates/nu-command/src/filters/prepend.rs b/crates/nu-command/src/filters/prepend.rs index d9b56c955..ea3699b9f 100644 --- a/crates/nu-command/src/filters/prepend.rs +++ b/crates/nu-command/src/filters/prepend.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,10 @@ impl Command for Prepend { fn signature(&self) -> nu_protocol::Signature { Signature::build("prepend") + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + )]) .required( "row", SyntaxShape::Any, diff --git a/crates/nu-command/src/filters/range.rs b/crates/nu-command/src/filters/range.rs index e2169fb66..431ceca31 100644 --- a/crates/nu-command/src/filters/range.rs +++ b/crates/nu-command/src/filters/range.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::{Call, RangeInclusion}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -17,6 +17,10 @@ impl Command for Range { fn signature(&self) -> Signature { Signature::build("range") + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + )]) .required( "rows", SyntaxShape::Range, diff --git a/crates/nu-command/src/filters/reduce.rs b/crates/nu-command/src/filters/reduce.rs index 902f3ff7f..8eef1e993 100644 --- a/crates/nu-command/src/filters/reduce.rs +++ b/crates/nu-command/src/filters/reduce.rs @@ -5,7 +5,7 @@ use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ - Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -18,6 +18,7 @@ impl Command for Reduce { fn signature(&self) -> Signature { Signature::build("reduce") + .input_output_types(vec![(Type::List(Box::new(Type::Any)), Type::Any)]) .named( "fold", SyntaxShape::Any, diff --git a/crates/nu-command/src/filters/reject.rs b/crates/nu-command/src/filters/reject.rs index a7029638c..9481a83d0 100644 --- a/crates/nu-command/src/filters/reject.rs +++ b/crates/nu-command/src/filters/reject.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; #[derive(Clone)] @@ -16,6 +16,10 @@ impl Command for Reject { fn signature(&self) -> Signature { Signature::build("reject") + .input_output_types(vec![ + (Type::Record(vec![]), Type::Record(vec![])), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .rest( "rest", SyntaxShape::CellPath, @@ -25,7 +29,7 @@ impl Command for Reject { } fn usage(&self) -> &str { - "Remove the given columns from the table. If you want to remove rows, try 'drop'." + "Remove the given columns from the table. To remove rows, use 'drop'." } fn run( @@ -43,13 +47,25 @@ impl Command for Reject { fn examples(&self) -> Vec { vec![ Example { - description: "Lists the files in a directory without showing the modified column", + description: "Reject a column in the `ls` table", example: "ls | reject modified", result: None, }, + Example { + description: "Reject a column in a table", + example: "[[a, b]; [1, 2]] | reject a", + result: Some(Value::List { + vals: vec![Value::Record { + cols: vec!["b".to_string()], + vals: vec![Value::test_int(2)], + span: Span::test_data(), + }], + span: Span::test_data(), + }), + }, Example { description: "Reject the specified field in a record", - example: "echo {a: 1, b: 2} | reject a", + example: "{a: 1, b: 2} | reject a", result: Some(Value::Record { cols: vec!["b".into()], vals: vec![Value::Int { @@ -61,7 +77,7 @@ impl Command for Reject { }, Example { description: "Reject a nested field in a record", - example: "echo {a: {b: 3,c: 5}} | reject a.b", + example: "{a: {b: 3, c: 5}} | reject a.b", result: Some(Value::Record { cols: vec!["a".into()], vals: vec![Value::Record { diff --git a/crates/nu-command/src/filters/rename.rs b/crates/nu-command/src/filters/rename.rs index 32fa9b368..4c90f6cce 100644 --- a/crates/nu-command/src/filters/rename.rs +++ b/crates/nu-command/src/filters/rename.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -15,6 +15,10 @@ impl Command for Rename { fn signature(&self) -> Signature { Signature::build("rename") + .input_output_types(vec![ + (Type::Record(vec![]), Type::Record(vec![])), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .named( "column", SyntaxShape::List(Box::new(SyntaxShape::String)), @@ -77,6 +81,15 @@ impl Command for Rename { span: Span::test_data(), }), }, + Example { + description: "Rename the fields of a record", + example: "{a: 1 b: 2} | rename x y", + result: Some(Value::Record { + cols: vec!["x".to_string(), "y".to_string()], + vals: vec![Value::test_int(1), Value::test_int(2)], + span: Span::test_data(), + }), + }, ] } } diff --git a/crates/nu-command/src/filters/reverse.rs b/crates/nu-command/src/filters/reverse.rs index 566871ccd..c21439918 100644 --- a/crates/nu-command/src/filters/reverse.rs +++ b/crates/nu-command/src/filters/reverse.rs @@ -2,7 +2,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - Value, + Type, Value, }; #[derive(Clone)] @@ -14,11 +14,19 @@ impl Command for Reverse { } fn signature(&self) -> nu_protocol::Signature { - Signature::build("reverse").category(Category::Filters) + Signature::build("reverse") + .input_output_types(vec![ + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + (Type::Table(vec![]), Type::Table(vec![])), + ]) + .category(Category::Filters) } fn usage(&self) -> &str { - "Reverses the table." + "Reverses the input list or table." } fn search_terms(&self) -> Vec<&str> { @@ -26,19 +34,32 @@ impl Command for Reverse { } fn examples(&self) -> Vec { - vec![Example { - example: "[0,1,2,3] | reverse", - description: "Reverse the items", - result: Some(Value::List { - vals: vec![ - Value::test_int(3), - Value::test_int(2), - Value::test_int(1), - Value::test_int(0), - ], - span: Span::test_data(), - }), - }] + vec![ + Example { + example: "[0,1,2,3] | reverse", + description: "Reverse a list", + result: Some(Value::List { + vals: vec![ + Value::test_int(3), + Value::test_int(2), + Value::test_int(1), + Value::test_int(0), + ], + span: Span::test_data(), + }), + }, + Example { + example: "[{a: 1} {a: 2}] | reverse", + description: "Reverse a table", + result: Some(Value::List { + vals: vec![ + Value::test_record(vec!["a"], vec![Value::test_int(2)]), + Value::test_record(vec!["a"], vec![Value::test_int(1)]), + ], + span: Span::test_data(), + }), + }, + ] } fn run( diff --git a/crates/nu-command/src/filters/roll/roll_down.rs b/crates/nu-command/src/filters/roll/roll_down.rs index 420747118..770108d83 100644 --- a/crates/nu-command/src/filters/roll/roll_down.rs +++ b/crates/nu-command/src/filters/roll/roll_down.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; use super::{vertical_rotate_value, VerticalDirection}; @@ -22,6 +22,8 @@ impl Command for RollDown { fn signature(&self) -> Signature { Signature::build(self.name()) + // TODO: It also operates on List + .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) .named("by", SyntaxShape::Int, "Number of rows to roll", Some('b')) .category(Category::Filters) } @@ -33,7 +35,7 @@ impl Command for RollDown { fn examples(&self) -> Vec { let columns = vec!["a".to_string(), "b".to_string()]; vec![Example { - description: "Rolls rows down", + description: "Rolls rows down of a table", example: "[[a b]; [1 2] [3 4] [5 6]] | roll down", result: Some(Value::List { vals: vec![ diff --git a/crates/nu-command/src/filters/roll/roll_left.rs b/crates/nu-command/src/filters/roll/roll_left.rs index 19c0957b6..28422745d 100644 --- a/crates/nu-command/src/filters/roll/roll_left.rs +++ b/crates/nu-command/src/filters/roll/roll_left.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; use super::{horizontal_rotate_value, HorizontalDirection}; @@ -22,6 +22,7 @@ impl Command for RollLeft { fn signature(&self) -> Signature { Signature::build(self.name()) + .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) .named( "by", SyntaxShape::Int, diff --git a/crates/nu-command/src/filters/roll/roll_right.rs b/crates/nu-command/src/filters/roll/roll_right.rs index e6759e725..c1554302d 100644 --- a/crates/nu-command/src/filters/roll/roll_right.rs +++ b/crates/nu-command/src/filters/roll/roll_right.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; use super::{horizontal_rotate_value, HorizontalDirection}; @@ -22,6 +22,7 @@ impl Command for RollRight { fn signature(&self) -> Signature { Signature::build(self.name()) + .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) .named( "by", SyntaxShape::Int, diff --git a/crates/nu-command/src/filters/roll/roll_up.rs b/crates/nu-command/src/filters/roll/roll_up.rs index 38b843fe0..5d136d154 100644 --- a/crates/nu-command/src/filters/roll/roll_up.rs +++ b/crates/nu-command/src/filters/roll/roll_up.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; use super::{vertical_rotate_value, VerticalDirection}; @@ -22,6 +22,8 @@ impl Command for RollUp { fn signature(&self) -> Signature { Signature::build(self.name()) + // TODO: It also operates on List + .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) .named("by", SyntaxShape::Int, "Number of rows to roll", Some('b')) .category(Category::Filters) } diff --git a/crates/nu-command/src/filters/rotate.rs b/crates/nu-command/src/filters/rotate.rs index 2d4cced01..a500e28db 100644 --- a/crates/nu-command/src/filters/rotate.rs +++ b/crates/nu-command/src/filters/rotate.rs @@ -3,7 +3,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; #[derive(Clone)] @@ -16,6 +16,7 @@ impl Command for Rotate { fn signature(&self) -> Signature { Signature::build("rotate") + .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) .switch("ccw", "rotate counter clockwise", None) .rest( "rest", diff --git a/crates/nu-command/src/filters/select.rs b/crates/nu-command/src/filters/select.rs index 05c40a45d..927e3de74 100644 --- a/crates/nu-command/src/filters/select.rs +++ b/crates/nu-command/src/filters/select.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, - PipelineIterator, ShellError, Signature, Span, SyntaxShape, Value, + PipelineIterator, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -17,6 +17,10 @@ impl Command for Select { // FIXME: also add support for --skip fn signature(&self) -> Signature { Signature::build("select") + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + (Type::Record(vec![]), Type::Record(vec![])), + ]) .switch( "ignore-errors", "when a column has empty cells, instead of erroring out, replace them with nothing", @@ -54,6 +58,19 @@ impl Command for Select { fn examples(&self) -> Vec { vec![ + Example { + description: "Select a column in a table", + example: "[{a: a b: b}] | select a", + result: Some(Value::List { + vals: vec![Value::test_record(vec!["a"], vec![Value::test_string("a")])], + span: Span::test_data(), + }), + }, + Example { + description: "Select a field in a record", + example: "{a: a b: b} | select a", + result: Some(Value::test_record(vec!["a"], vec![Value::test_string("a")])), + }, Example { description: "Select just the name column", example: "ls | select name", @@ -259,3 +276,15 @@ impl Iterator for NthIterator { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(Select) + } +} diff --git a/crates/nu-command/src/filters/shuffle.rs b/crates/nu-command/src/filters/shuffle.rs index 47e23d722..81bbd70cf 100644 --- a/crates/nu-command/src/filters/shuffle.rs +++ b/crates/nu-command/src/filters/shuffle.rs @@ -1,7 +1,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, + Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type, }; use rand::prelude::SliceRandom; use rand::thread_rng; @@ -15,7 +15,12 @@ impl Command for Shuffle { } fn signature(&self) -> nu_protocol::Signature { - Signature::build("shuffle").category(Category::Filters) + Signature::build("shuffle") + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + )]) + .category(Category::Filters) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/filters/skip/skip_.rs b/crates/nu-command/src/filters/skip/skip_.rs index c1c2c0315..cd7ebf28a 100644 --- a/crates/nu-command/src/filters/skip/skip_.rs +++ b/crates/nu-command/src/filters/skip/skip_.rs @@ -5,7 +5,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, SyntaxShape, Value, + Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -18,6 +18,13 @@ impl Command for Skip { fn signature(&self) -> Signature { Signature::build(self.name()) + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ]) .optional("n", SyntaxShape::Int, "the number of elements to skip") .category(Category::Filters) } @@ -31,13 +38,21 @@ impl Command for Skip { } fn search_terms(&self) -> Vec<&str> { - vec!["ignore", "remove"] + vec!["ignore", "remove", "last", "slice", "tail"] } fn examples(&self) -> Vec { vec![ Example { - description: "Skip two elements", + description: "Skip the first value of a list", + example: "echo [2 4 6 8] | skip 1", + result: Some(Value::List { + vals: vec![Value::test_int(4), Value::test_int(6), Value::test_int(8)], + span: Span::test_data(), + }), + }, + Example { + description: "Skip two rows of a table", example: "echo [[editions]; [2015] [2018] [2021]] | skip 2", result: Some(Value::List { vals: vec![Value::Record { @@ -48,14 +63,6 @@ impl Command for Skip { span: Span::test_data(), }), }, - Example { - description: "Skip the first value", - example: "echo [2 4 6 8] | skip", - result: Some(Value::List { - vals: vec![Value::test_int(4), Value::test_int(6), Value::test_int(8)], - span: Span::test_data(), - }), - }, ] } diff --git a/crates/nu-command/src/filters/skip/skip_until.rs b/crates/nu-command/src/filters/skip/skip_until.rs index 75afa69b8..0d0dbfb49 100644 --- a/crates/nu-command/src/filters/skip/skip_until.rs +++ b/crates/nu-command/src/filters/skip/skip_until.rs @@ -3,7 +3,7 @@ use nu_protocol::{ ast::Call, engine::{CaptureBlock, Command, EngineState, Stack}, Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,13 @@ impl Command for SkipUntil { fn signature(&self) -> Signature { Signature::build(self.name()) + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ]) .required( "predicate", SyntaxShape::RowCondition, @@ -33,14 +40,27 @@ impl Command for SkipUntil { } fn examples(&self) -> Vec { - vec![Example { - description: "Skip until the element is positive", - example: "echo [-2 0 2 -1] | skip until $it > 0", - result: Some(Value::List { - vals: vec![Value::test_int(2), Value::test_int(-1)], - span: Span::test_data(), - }), - }] + vec![ + Example { + description: "Skip until the element is positive", + example: "[-2 0 2 -1] | skip until $it > 0", + result: Some(Value::List { + vals: vec![Value::test_int(2), Value::test_int(-1)], + span: Span::test_data(), + }), + }, + Example { + description: "Skip until the field value is positive", + example: "[{a: -2} {a: 0} {a: 2} {a: -1}] | skip until $it.a > 0", + result: Some(Value::List { + vals: vec![ + Value::test_record(vec!["a"], vec![Value::test_int(2)]), + Value::test_record(vec!["a"], vec![Value::test_int(-1)]), + ], + span: Span::test_data(), + }), + }, + ] } fn run( diff --git a/crates/nu-command/src/filters/skip/skip_while.rs b/crates/nu-command/src/filters/skip/skip_while.rs index e43b5c2ed..2ce137d2d 100644 --- a/crates/nu-command/src/filters/skip/skip_while.rs +++ b/crates/nu-command/src/filters/skip/skip_while.rs @@ -3,7 +3,7 @@ use nu_protocol::{ ast::Call, engine::{CaptureBlock, Command, EngineState, Stack}, Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,13 @@ impl Command for SkipWhile { fn signature(&self) -> Signature { Signature::build(self.name()) + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ]) .required( "predicate", SyntaxShape::RowCondition, @@ -33,14 +40,28 @@ impl Command for SkipWhile { } fn examples(&self) -> Vec { - vec![Example { - description: "Skip while the element is negative", - example: "echo [-2 0 2 -1] | skip while $it < 0", - result: Some(Value::List { - vals: vec![Value::test_int(0), Value::test_int(2), Value::test_int(-1)], - span: Span::test_data(), - }), - }] + vec![ + Example { + description: "Skip while the element is negative", + example: "[-2 0 2 -1] | skip while $it < 0", + result: Some(Value::List { + vals: vec![Value::test_int(0), Value::test_int(2), Value::test_int(-1)], + span: Span::test_data(), + }), + }, + Example { + description: "Skip while the field value is negative", + example: "[{a: -2} {a: 0} {a: 2} {a: -1}] | skip while $it.a < 0", + result: Some(Value::List { + vals: vec![ + Value::test_record(vec!["a"], vec![Value::test_int(0)]), + Value::test_record(vec!["a"], vec![Value::test_int(2)]), + Value::test_record(vec!["a"], vec![Value::test_int(-1)]), + ], + span: Span::test_data(), + }), + }, + ] } fn run( diff --git a/crates/nu-command/src/filters/sort.rs b/crates/nu-command/src/filters/sort.rs index 065c3c33a..a7a982df1 100644 --- a/crates/nu-command/src/filters/sort.rs +++ b/crates/nu-command/src/filters/sort.rs @@ -2,7 +2,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, Value, + Signature, Span, Type, Value, }; use std::cmp::Ordering; @@ -16,7 +16,11 @@ impl Command for Sort { fn signature(&self) -> nu_protocol::Signature { Signature::build("sort") - .switch("reverse", "Sort in reverse order", Some('r')) + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), (Type::Record(vec![]), Type::Record(vec![])),]) + .switch("reverse", "Sort in reverse order", Some('r')) .switch( "insensitive", "Sort string-based columns case-insensitively", diff --git a/crates/nu-command/src/filters/sort_by.rs b/crates/nu-command/src/filters/sort_by.rs index 35c20511f..aa3f4f397 100644 --- a/crates/nu-command/src/filters/sort_by.rs +++ b/crates/nu-command/src/filters/sort_by.rs @@ -3,7 +3,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +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![])), + ( + 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( diff --git a/crates/nu-command/src/filters/split_by.rs b/crates/nu-command/src/filters/split_by.rs index 25c164079..93b328feb 100644 --- a/crates/nu-command/src/filters/split_by.rs +++ b/crates/nu-command/src/filters/split_by.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -14,11 +14,9 @@ impl Command for SplitBy { } fn signature(&self) -> Signature { - Signature::build("split-by").optional( - "splitter", - SyntaxShape::Any, - "the splitter value to use", - ) + Signature::build("split-by") + .input_output_types(vec![(Type::Record(vec![]), Type::Record(vec![]))]) + .optional("splitter", SyntaxShape::Any, "the splitter value to use") } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/filters/take/take_.rs b/crates/nu-command/src/filters/take/take_.rs index a99d651ca..356c42675 100644 --- a/crates/nu-command/src/filters/take/take_.rs +++ b/crates/nu-command/src/filters/take/take_.rs @@ -16,6 +16,13 @@ impl Command for Take { fn signature(&self) -> Signature { Signature::build("take") + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ]) .required( "n", SyntaxShape::Int, @@ -28,6 +35,10 @@ impl Command for Take { "Take only the first n elements." } + fn search_terms(&self) -> Vec<&str> { + vec!["first", "slice", "head"] + } + fn run( &self, engine_state: &EngineState, @@ -56,6 +67,17 @@ impl Command for Take { span: Span::test_data(), }), }, + Example { + description: "Return the first two rows of a table", + example: "echo [[editions]; [2015] [2018] [2021]] | take 2", + result: Some(Value::List { + vals: vec![ + Value::test_record(vec!["editions"], vec![Value::test_int(2015)]), + Value::test_record(vec!["editions"], vec![Value::test_int(2018)]), + ], + span: Span::test_data(), + }), + }, ] } } diff --git a/crates/nu-command/src/filters/take/take_until.rs b/crates/nu-command/src/filters/take/take_until.rs index bddc251c3..9a1c4c018 100644 --- a/crates/nu-command/src/filters/take/take_until.rs +++ b/crates/nu-command/src/filters/take/take_until.rs @@ -3,7 +3,7 @@ use nu_protocol::{ ast::Call, engine::{CaptureBlock, Command, EngineState, Stack}, Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,13 @@ impl Command for TakeUntil { fn signature(&self) -> Signature { Signature::build(self.name()) + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ]) .required( "predicate", SyntaxShape::RowCondition, @@ -29,14 +36,27 @@ impl Command for TakeUntil { } fn examples(&self) -> Vec { - vec![Example { - description: "Take until the element is positive", - example: "echo [-1 -2 9 1] | take until $it > 0", - result: Some(Value::List { - vals: vec![Value::test_int(-1), Value::test_int(-2)], - span: Span::test_data(), - }), - }] + vec![ + Example { + description: "Take until the element is positive", + example: "[-1 -2 9 1] | take until $it > 0", + result: Some(Value::List { + vals: vec![Value::test_int(-1), Value::test_int(-2)], + span: Span::test_data(), + }), + }, + Example { + description: "Take until the field value is positive", + example: "[{a: -1} {a: -2} {a: 9} {a: 1}] | take until $it.a > 0", + result: Some(Value::List { + vals: vec![ + Value::test_record(vec!["a"], vec![Value::test_int(-1)]), + Value::test_record(vec!["a"], vec![Value::test_int(-2)]), + ], + span: Span::test_data(), + }), + }, + ] } fn run( diff --git a/crates/nu-command/src/filters/take/take_while.rs b/crates/nu-command/src/filters/take/take_while.rs index 67c7aa383..6b4370902 100644 --- a/crates/nu-command/src/filters/take/take_while.rs +++ b/crates/nu-command/src/filters/take/take_while.rs @@ -3,7 +3,7 @@ use nu_protocol::{ ast::Call, engine::{CaptureBlock, Command, EngineState, Stack}, Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,13 @@ impl Command for TakeWhile { fn signature(&self) -> Signature { Signature::build(self.name()) + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ]) .required( "predicate", SyntaxShape::RowCondition, @@ -29,14 +36,27 @@ impl Command for TakeWhile { } fn examples(&self) -> Vec { - vec![Example { - description: "Take while the element is negative", - example: "echo [-1 -2 9 1] | take while $it < 0", - result: Some(Value::List { - vals: vec![Value::test_int(-1), Value::test_int(-2)], - span: Span::test_data(), - }), - }] + vec![ + Example { + description: "Take while the element is negative", + example: "[-1 -2 9 1] | take while $it < 0", + result: Some(Value::List { + vals: vec![Value::test_int(-1), Value::test_int(-2)], + span: Span::test_data(), + }), + }, + Example { + description: "Take while the field value is negative", + example: "[{a: -1} {a: -2} {a: 9} {a: 1}] | take while $it.a < 0", + result: Some(Value::List { + vals: vec![ + Value::test_record(vec!["a"], vec![Value::test_int(-1)]), + Value::test_record(vec!["a"], vec![Value::test_int(-2)]), + ], + span: Span::test_data(), + }), + }, + ] } fn run( diff --git a/crates/nu-command/src/filters/transpose.rs b/crates/nu-command/src/filters/transpose.rs index 0a3761466..2ec7166fa 100644 --- a/crates/nu-command/src/filters/transpose.rs +++ b/crates/nu-command/src/filters/transpose.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, Spanned, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -26,6 +26,10 @@ impl Command for Transpose { fn signature(&self) -> Signature { Signature::build("transpose") + .input_output_types(vec![ + (Type::Table(vec![]), Type::Table(vec![])), + (Type::Table(vec![]), Type::Record(vec![])), + ]) .switch( "header-row", "treat the first row as column names", @@ -81,7 +85,7 @@ impl Command for Transpose { vec![ Example { description: "Transposes the table contents with default column names", - example: "echo [[c1 c2]; [1 2]] | transpose", + example: "[[c1 c2]; [1 2]] | transpose", result: Some(Value::List { vals: vec![ Value::Record { @@ -100,7 +104,7 @@ impl Command for Transpose { }, Example { description: "Transposes the table contents with specified column names", - example: "echo [[c1 c2]; [1 2]] | transpose key val", + example: "[[c1 c2]; [1 2]] | transpose key val", result: Some(Value::List { vals: vec![ Value::Record { @@ -120,7 +124,7 @@ impl Command for Transpose { Example { description: "Transposes the table without column names and specify a new column name", - example: "echo [[c1 c2]; [1 2]] | transpose -i val", + example: "[[c1 c2]; [1 2]] | transpose -i val", result: Some(Value::List { vals: vec![ Value::Record { @@ -139,7 +143,7 @@ impl Command for Transpose { }, Example { description: "Transfer back to record with -d flag", - example: "echo {c1: 1, c2: 2} | transpose | transpose -i -r -d", + example: "{c1: 1, c2: 2} | transpose | transpose -i -r -d", result: Some(Value::Record { cols: vec!["c1".to_string(), "c2".to_string()], vals: vec![Value::test_int(1), Value::test_int(2)], diff --git a/crates/nu-command/src/filters/uniq.rs b/crates/nu-command/src/filters/uniq.rs index f64e3b7f3..30ff0963d 100644 --- a/crates/nu-command/src/filters/uniq.rs +++ b/crates/nu-command/src/filters/uniq.rs @@ -2,7 +2,9 @@ use std::collections::VecDeque; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, IntoPipelineData, PipelineData, Signature, Span, Value}; +use nu_protocol::{ + Category, Example, IntoPipelineData, PipelineData, Signature, Span, Type, Value, +}; #[derive(Clone)] pub struct Uniq; @@ -14,6 +16,17 @@ impl Command for Uniq { fn signature(&self) -> Signature { Signature::build("uniq") + .input_output_types(vec![ + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + ( + // -c + Type::List(Box::new(Type::Any)), + Type::Table(vec![]), + ), + ]) .switch( "count", "Return a table containing the distinct input values together with their counts", diff --git a/crates/nu-command/src/filters/update.rs b/crates/nu-command/src/filters/update.rs index 5087e68f3..027eb65bd 100644 --- a/crates/nu-command/src/filters/update.rs +++ b/crates/nu-command/src/filters/update.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember}; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, - ShellError, Signature, Span, SyntaxShape, Value, + ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,7 @@ impl Command for Update { fn signature(&self) -> Signature { Signature::build("update") + .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) .required( "field", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/filters/update_cells.rs b/crates/nu-command/src/filters/update_cells.rs index 6dc33b6b9..4fc8e8d6d 100644 --- a/crates/nu-command/src/filters/update_cells.rs +++ b/crates/nu-command/src/filters/update_cells.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::{Block, Call}; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, - PipelineIterator, ShellError, Signature, Span, SyntaxShape, Value, + PipelineIterator, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use std::collections::HashSet; use std::iter::FromIterator; @@ -18,6 +18,7 @@ impl Command for UpdateCells { fn signature(&self) -> Signature { Signature::build("update cells") + .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) .required( "block", SyntaxShape::Block(Some(vec![SyntaxShape::Any])), diff --git a/crates/nu-command/src/filters/upsert.rs b/crates/nu-command/src/filters/upsert.rs index 4472177f5..ac8570156 100644 --- a/crates/nu-command/src/filters/upsert.rs +++ b/crates/nu-command/src/filters/upsert.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, CellPath, PathMember}; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, - ShellError, Signature, Span, SyntaxShape, Value, + ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,7 @@ impl Command for Upsert { fn signature(&self) -> Signature { Signature::build("upsert") + .input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))]) .required( "field", SyntaxShape::CellPath, @@ -50,19 +51,19 @@ impl Command for Upsert { fn examples(&self) -> Vec { vec![Example { description: "Update a column value", - example: "echo {'name': 'nu', 'stars': 5} | upsert name 'Nushell'", + example: "{'name': 'nu', 'stars': 5} | upsert name 'Nushell'", result: Some(Value::Record { cols: vec!["name".into(), "stars".into()], vals: vec![Value::test_string("Nushell"), Value::test_int(5)], span: Span::test_data()}), }, Example { description: "Insert a new column", - example: "echo {'name': 'nu', 'stars': 5} | upsert language 'Rust'", + example: "{'name': 'nu', 'stars': 5} | upsert language 'Rust'", result: Some(Value::Record { cols: vec!["name".into(), "stars".into(), "language".into()], vals: vec![Value::test_string("nu"), Value::test_int(5), Value::test_string("Rust")], span: Span::test_data()}), }, Example { description: "Use in block form for more involved updating logic", - example: "echo [[count fruit]; [1 'apple']] | upsert count {|f| $f.count + 1}", + example: "[[count fruit]; [1 'apple']] | upsert count {|f| $f.count + 1}", result: Some(Value::List { vals: vec![Value::Record { cols: vec!["count".into(), "fruit".into()], vals: vec![Value::test_int(2), Value::test_string("apple")], span: Span::test_data()}], span: Span::test_data()}), }, Example { description: "Use in block form for more involved updating logic", - example: "echo [[project, authors]; ['nu', ['Andrés', 'JT', 'Yehuda']]] | upsert authors {|a| $a.authors | str join ','}", + example: "[[project, authors]; ['nu', ['Andrés', 'JT', 'Yehuda']]] | upsert authors {|a| $a.authors | str join ','}", result: Some(Value::List { vals: vec![Value::Record { cols: vec!["project".into(), "authors".into()], vals: vec![Value::test_string("nu"), Value::test_string("Andrés,JT,Yehuda")], span: Span::test_data()}], span: Span::test_data()}), }, Example { diff --git a/crates/nu-command/src/filters/where_.rs b/crates/nu-command/src/filters/where_.rs index 3a29668c9..73f5114e5 100644 --- a/crates/nu-command/src/filters/where_.rs +++ b/crates/nu-command/src/filters/where_.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, SyntaxShape, Value, + Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -21,6 +21,13 @@ impl Command for Where { fn signature(&self) -> nu_protocol::Signature { Signature::build("where") + .input_output_types(vec![ + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + ), + (Type::Table(vec![]), Type::Table(vec![])), + ]) .optional("cond", SyntaxShape::RowCondition, "condition") .named( "block", @@ -218,6 +225,26 @@ impl Command for Where { fn examples(&self) -> Vec { vec![ + Example { + description: "Filter rows of a table according to a condition", + example: "[{a: 1} {a: 2}] | where a > 1", + result: Some(Value::List { + vals: vec![Value::Record { + cols: vec!["a".to_string()], + vals: vec![Value::test_int(2)], + span: Span::test_data(), + }], + span: Span::test_data(), + }), + }, + Example { + description: "Filter items of a list according to a condition", + example: "[1 2] | where {|x| $x > 1}", + result: Some(Value::List { + vals: vec![Value::test_int(2)], + span: Span::test_data(), + }), + }, Example { description: "List all files in the current directory with sizes greater than 2kb", example: "ls | where size > 2kb", diff --git a/crates/nu-command/src/filters/window.rs b/crates/nu-command/src/filters/window.rs index 9da690bd2..40c18f1a8 100644 --- a/crates/nu-command/src/filters/window.rs +++ b/crates/nu-command/src/filters/window.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, PipelineData, Signature, Span, Spanned, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,10 @@ impl Command for Window { fn signature(&self) -> Signature { Signature::build("window") + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::List(Box::new(Type::Any)))), + )]) .required("window_size", SyntaxShape::Int, "the size of each window") .named( "stride", @@ -155,7 +159,7 @@ impl Command for Window { vec![ Example { - example: "echo [1 2 3 4] | window 2", + example: "[1 2 3 4] | window 2", description: "A sliding window of two elements", result: Some(Value::List { vals: stream_test_1, diff --git a/crates/nu-command/src/filters/wrap.rs b/crates/nu-command/src/filters/wrap.rs index 2116dac56..6778d8722 100644 --- a/crates/nu-command/src/filters/wrap.rs +++ b/crates/nu-command/src/filters/wrap.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, - Span, SyntaxShape, Value, + Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -20,6 +20,7 @@ impl Command for Wrap { fn signature(&self) -> nu_protocol::Signature { Signature::build("wrap") + .input_output_types(vec![(Type::List(Box::new(Type::Any)), Type::Table(vec![]))]) .required("name", SyntaxShape::String, "the name of the column") .category(Category::Filters) } @@ -68,7 +69,7 @@ impl Command for Wrap { fn examples(&self) -> Vec { vec![Example { description: "Wrap a list into a table with a given column name", - example: "echo [1 2 3] | wrap num", + example: "[1 2 3] | wrap num", result: Some(Value::List { vals: vec![ Value::Record { diff --git a/crates/nu-command/src/filters/zip.rs b/crates/nu-command/src/filters/zip.rs index 95ad43f06..5415706c8 100644 --- a/crates/nu-command/src/filters/zip.rs +++ b/crates/nu-command/src/filters/zip.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, - Span, SyntaxShape, Value, + Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -20,6 +20,16 @@ impl Command for Zip { fn signature(&self) -> nu_protocol::Signature { Signature::build("zip") + .input_output_types(vec![ + ( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::List(Box::new(Type::Any)))), + ), + ( + Type::Range, + Type::List(Box::new(Type::List(Box::new(Type::Any)))), + ), + ]) .required("other", SyntaxShape::Any, "the other input") .category(Category::Filters) } @@ -67,14 +77,33 @@ impl Command for Zip { span: Span::test_data(), }; - vec![Example { - example: "1..3 | zip 4..6", - description: "Zip multiple streams and get one of the results", - result: Some(Value::List { - vals: vec![test_row_1, test_row_2, test_row_3], - span: Span::test_data(), - }), - }] + vec![ + Example { + example: "[1 2] | zip [3 4]", + description: "Zip two lists", + result: Some(Value::List { + vals: vec![ + Value::List { + vals: vec![Value::test_int(1), Value::test_int(3)], + span: Span::test_data(), + }, + Value::List { + vals: vec![Value::test_int(2), Value::test_int(4)], + span: Span::test_data(), + }, + ], + span: Span::test_data(), + }), + }, + Example { + example: "1..3 | zip 4..6", + description: "Zip two streams", + result: Some(Value::List { + vals: vec![test_row_1, test_row_2, test_row_3], + span: Span::test_data(), + }), + }, + ] } fn run( diff --git a/crates/nu-command/src/formats/from/csv.rs b/crates/nu-command/src/formats/from/csv.rs index 6da84f0be..e71a07b6b 100644 --- a/crates/nu-command/src/formats/from/csv.rs +++ b/crates/nu-command/src/formats/from/csv.rs @@ -3,7 +3,9 @@ use super::delimited::{from_delimited_data, trim_from_str}; use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Value}; +use nu_protocol::{ + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, +}; #[derive(Clone)] pub struct FromCsv; @@ -15,6 +17,7 @@ impl Command for FromCsv { fn signature(&self) -> Signature { Signature::build("from csv") + .input_output_types(vec![(Type::String, Type::Table(vec![]))]) .named( "separator", SyntaxShape::String, @@ -54,8 +57,18 @@ impl Command for FromCsv { vec![ Example { description: "Convert comma-separated data to a table", - example: "open data.txt | from csv", - result: None, + example: "\"ColA,ColB\n1,2\" | from csv", + result: Some(Value::List { + vals: vec![Value::Record { + cols: vec!["ColA".to_string(), "ColB".to_string()], + vals: vec![ + Value::test_int(1), + Value::test_int(2), + ], + span: Span::test_data(), + }], + span: Span::test_data(), + }) }, Example { description: "Convert comma-separated data to a table, ignoring headers", diff --git a/crates/nu-command/src/formats/from/eml.rs b/crates/nu-command/src/formats/from/eml.rs index 1880afc3b..2da207976 100644 --- a/crates/nu-command/src/formats/from/eml.rs +++ b/crates/nu-command/src/formats/from/eml.rs @@ -7,7 +7,7 @@ use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; use nu_protocol::Config; use nu_protocol::{ - Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -22,6 +22,7 @@ impl Command for FromEml { fn signature(&self) -> Signature { Signature::build("from eml") + .input_output_types(vec![(Type::String, Type::Record(vec![]))]) .named( "preview-body", SyntaxShape::Int, @@ -32,7 +33,7 @@ impl Command for FromEml { } fn usage(&self) -> &str { - "Parse text as .eml and create table." + "Parse text as .eml and create record." } fn run( @@ -52,7 +53,7 @@ impl Command for FromEml { fn examples(&self) -> Vec { vec![ Example { - description: "Convert eml structured data into table", + description: "Convert eml structured data into record", example: "'From: test@email.com Subject: Welcome To: someone@somewhere.com @@ -89,7 +90,7 @@ Test' | from eml", }), }, Example { - description: "Convert eml structured data into table", + description: "Convert eml structured data into record", example: "'From: test@email.com Subject: Welcome To: someone@somewhere.com diff --git a/crates/nu-command/src/formats/from/ics.rs b/crates/nu-command/src/formats/from/ics.rs index 382d54935..5a3990329 100644 --- a/crates/nu-command/src/formats/from/ics.rs +++ b/crates/nu-command/src/formats/from/ics.rs @@ -6,7 +6,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, - Spanned, Value, + Spanned, Type, Value, }; use std::io::BufReader; @@ -19,7 +19,9 @@ impl Command for FromIcs { } fn signature(&self) -> Signature { - Signature::build("from ics").category(Category::Formats) + Signature::build("from ics") + .input_output_types(vec![(Type::String, Type::Table(vec![]))]) + .category(Category::Formats) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/formats/from/ini.rs b/crates/nu-command/src/formats/from/ini.rs index 4a40e1c36..7c31d342f 100644 --- a/crates/nu-command/src/formats/from/ini.rs +++ b/crates/nu-command/src/formats/from/ini.rs @@ -2,7 +2,8 @@ use indexmap::map::IndexMap; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value, + Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, + Value, }; #[derive(Clone)] @@ -14,11 +15,13 @@ impl Command for FromIni { } fn signature(&self) -> Signature { - Signature::build("from ini").category(Category::Formats) + Signature::build("from ini") + .input_output_types(vec![(Type::String, Type::Record(vec![]))]) + .category(Category::Formats) } fn usage(&self) -> &str { - "Parse text as .ini and create table" + "Parse text as .ini and create record" } fn examples(&self) -> Vec { @@ -26,7 +29,7 @@ impl Command for FromIni { example: "'[foo] a=1 b=2' | from ini", - description: "Converts ini formatted string to table", + description: "Converts ini formatted string to record", result: Some(Value::Record { cols: vec!["foo".to_string()], vals: vec![Value::Record { diff --git a/crates/nu-command/src/formats/from/json.rs b/crates/nu-command/src/formats/from/json.rs index 838392370..1ddaecb6e 100644 --- a/crates/nu-command/src/formats/from/json.rs +++ b/crates/nu-command/src/formats/from/json.rs @@ -2,7 +2,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, - Signature, Span, Value, + Signature, Span, Type, Value, }; #[derive(Clone)] @@ -19,6 +19,7 @@ impl Command for FromJson { fn signature(&self) -> nu_protocol::Signature { Signature::build("from json") + .input_output_types(vec![(Type::String, Type::Any)]) .switch("objects", "treat each line as a separate value", Some('o')) .category(Category::Formats) } diff --git a/crates/nu-command/src/formats/from/nuon.rs b/crates/nu-command/src/formats/from/nuon.rs index 050c52be2..a11dfc010 100644 --- a/crates/nu-command/src/formats/from/nuon.rs +++ b/crates/nu-command/src/formats/from/nuon.rs @@ -17,7 +17,9 @@ impl Command for FromNuon { } fn signature(&self) -> nu_protocol::Signature { - Signature::build("from nuon").category(Category::Experimental) + Signature::build("from nuon") + .input_output_types(vec![(Type::String, Type::Any)]) + .category(Category::Experimental) } fn examples(&self) -> Vec { diff --git a/crates/nu-command/src/formats/from/ods.rs b/crates/nu-command/src/formats/from/ods.rs index d309158e8..c2e5c710b 100644 --- a/crates/nu-command/src/formats/from/ods.rs +++ b/crates/nu-command/src/formats/from/ods.rs @@ -4,7 +4,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use std::io::Cursor; @@ -18,6 +18,8 @@ impl Command for FromOds { fn signature(&self) -> Signature { Signature::build("from ods") + .input_output_types(vec![(Type::String, Type::Table(vec![]))]) + .allow_variants_without_examples(true) .named( "sheets", SyntaxShape::List(Box::new(SyntaxShape::String)), diff --git a/crates/nu-command/src/formats/from/ssv.rs b/crates/nu-command/src/formats/from/ssv.rs index 3efa0873f..ff34b1fea 100644 --- a/crates/nu-command/src/formats/from/ssv.rs +++ b/crates/nu-command/src/formats/from/ssv.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -19,6 +19,7 @@ impl Command for FromSsv { fn signature(&self) -> Signature { Signature::build("from ssv") + .input_output_types(vec![(Type::String, Type::Table(vec![]))]) .switch( "noheaders", "don't treat the first row as column names", diff --git a/crates/nu-command/src/formats/from/toml.rs b/crates/nu-command/src/formats/from/toml.rs index 6c3d5a6f9..2c52fa0d7 100644 --- a/crates/nu-command/src/formats/from/toml.rs +++ b/crates/nu-command/src/formats/from/toml.rs @@ -1,7 +1,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value, + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value, }; #[derive(Clone)] @@ -13,18 +13,20 @@ impl Command for FromToml { } fn signature(&self) -> Signature { - Signature::build("from toml").category(Category::Formats) + Signature::build("from toml") + .input_output_types(vec![(Type::String, Type::Record(vec![]))]) + .category(Category::Formats) } fn usage(&self) -> &str { - "Parse text as .toml and create table." + "Parse text as .toml and create record." } fn examples(&self) -> Vec { vec![ Example { example: "'a = 1' | from toml", - description: "Converts toml formatted string to table", + description: "Converts toml formatted string to record", result: Some(Value::Record { cols: vec!["a".to_string()], vals: vec![Value::Int { @@ -37,7 +39,7 @@ impl Command for FromToml { Example { example: "'a = 1 b = [1, 2]' | from toml", - description: "Converts toml formatted string to table", + description: "Converts toml formatted string to record", result: Some(Value::Record { cols: vec!["a".to_string(), "b".to_string()], vals: vec![ diff --git a/crates/nu-command/src/formats/from/tsv.rs b/crates/nu-command/src/formats/from/tsv.rs index 270199052..295daa35f 100644 --- a/crates/nu-command/src/formats/from/tsv.rs +++ b/crates/nu-command/src/formats/from/tsv.rs @@ -3,7 +3,9 @@ use super::delimited::{from_delimited_data, trim_from_str}; use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Value}; +use nu_protocol::{ + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, +}; #[derive(Clone)] pub struct FromTsv; @@ -15,6 +17,7 @@ impl Command for FromTsv { fn signature(&self) -> Signature { Signature::build("from tsv") + .input_output_types(vec![(Type::String, Type::Table(vec![]))]) .switch( "noheaders", "don't treat the first row as column names", @@ -46,6 +49,21 @@ impl Command for FromTsv { fn examples(&self) -> Vec { vec![ + Example { + description: "Convert tab-separated data to a table", + example: "\"ColA\tColB\n1\t2\" | from tsv", + result: Some(Value::List { + vals: vec![Value::Record { + cols: vec!["ColA".to_string(), "ColB".to_string()], + vals: vec![ + Value::test_int(1), + Value::test_int(2), + ], + span: Span::test_data(), + }], + span: Span::test_data(), + }) + }, Example { description: "Create a tsv file with header columns and open it", example: r#"$'c1(char tab)c2(char tab)c3(char nl)1(char tab)2(char tab)3' | save tsv-data | open tsv-data | from tsv"#, diff --git a/crates/nu-command/src/formats/from/url.rs b/crates/nu-command/src/formats/from/url.rs index 630f57f22..2b89956d8 100644 --- a/crates/nu-command/src/formats/from/url.rs +++ b/crates/nu-command/src/formats/from/url.rs @@ -1,6 +1,8 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Config, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{ + Category, Config, Example, PipelineData, ShellError, Signature, Span, Type, Value, +}; #[derive(Clone)] pub struct FromUrl; @@ -11,11 +13,13 @@ impl Command for FromUrl { } fn signature(&self) -> Signature { - Signature::build("from url").category(Category::Formats) + Signature::build("from url") + .input_output_types(vec![(Type::String, Type::Record(vec![]))]) + .category(Category::Formats) } fn usage(&self) -> &str { - "Parse url-encoded string as a table." + "Parse url-encoded string as a record." } fn run( @@ -33,7 +37,7 @@ impl Command for FromUrl { fn examples(&self) -> Vec { vec![Example { example: "'bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter' | from url", - description: "Convert url encoded string into a table", + description: "Convert url encoded string into a record", result: Some(Value::Record { cols: vec![ "bread".to_string(), diff --git a/crates/nu-command/src/formats/from/vcf.rs b/crates/nu-command/src/formats/from/vcf.rs index d6fd84f19..cb42c6e42 100644 --- a/crates/nu-command/src/formats/from/vcf.rs +++ b/crates/nu-command/src/formats/from/vcf.rs @@ -5,7 +5,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, - Spanned, Value, + Spanned, Type, Value, }; #[derive(Clone)] @@ -17,7 +17,9 @@ impl Command for FromVcf { } fn signature(&self) -> Signature { - Signature::build("from vcf").category(Category::Formats) + Signature::build("from vcf") + .input_output_types(vec![(Type::String, Type::Table(vec![]))]) + .category(Category::Formats) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/formats/from/xlsx.rs b/crates/nu-command/src/formats/from/xlsx.rs index c2594f84c..6e754c22a 100644 --- a/crates/nu-command/src/formats/from/xlsx.rs +++ b/crates/nu-command/src/formats/from/xlsx.rs @@ -4,7 +4,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use std::io::Cursor; @@ -18,6 +18,8 @@ impl Command for FromXlsx { fn signature(&self) -> Signature { Signature::build("from xlsx") + .input_output_types(vec![(Type::Binary, Type::Table(vec![]))]) + .allow_variants_without_examples(true) .named( "sheets", SyntaxShape::List(Box::new(SyntaxShape::String)), diff --git a/crates/nu-command/src/formats/from/xml.rs b/crates/nu-command/src/formats/from/xml.rs index 481f734b5..41fe153b1 100644 --- a/crates/nu-command/src/formats/from/xml.rs +++ b/crates/nu-command/src/formats/from/xml.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, - Spanned, Value, + Spanned, Type, Value, }; #[derive(Clone)] @@ -15,11 +15,13 @@ impl Command for FromXml { } fn signature(&self) -> Signature { - Signature::build("from xml").category(Category::Formats) + Signature::build("from xml") + .input_output_types(vec![(Type::String, Type::Record(vec![]))]) + .category(Category::Formats) } fn usage(&self) -> &str { - "Parse text as .xml and create table." + "Parse text as .xml and create record." } fn run( @@ -40,7 +42,7 @@ impl Command for FromXml { Event ' | from xml"#, - description: "Converts xml formatted string to table", + description: "Converts xml formatted string to record", result: Some(Value::Record { cols: vec!["note".to_string()], vals: vec![Value::Record { diff --git a/crates/nu-command/src/formats/from/yaml.rs b/crates/nu-command/src/formats/from/yaml.rs index d7242f3ef..3f07ae5ef 100644 --- a/crates/nu-command/src/formats/from/yaml.rs +++ b/crates/nu-command/src/formats/from/yaml.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, - Spanned, Value, + Spanned, Type, Value, }; use serde::de::Deserialize; use std::collections::HashMap; @@ -17,7 +17,9 @@ impl Command for FromYaml { } fn signature(&self) -> Signature { - Signature::build("from yaml").category(Category::Formats) + Signature::build("from yaml") + .input_output_types(vec![(Type::String, Type::Any)]) + .category(Category::Formats) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/formats/to/csv.rs b/crates/nu-command/src/formats/to/csv.rs index 2ad25294e..97498420a 100644 --- a/crates/nu-command/src/formats/to/csv.rs +++ b/crates/nu-command/src/formats/to/csv.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Config, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, - Value, + Type, Value, }; #[derive(Clone)] @@ -17,6 +17,7 @@ impl Command for ToCsv { fn signature(&self) -> Signature { Signature::build("to csv") + .input_output_types(vec![(Type::Any, Type::String)]) .named( "separator", SyntaxShape::String, diff --git a/crates/nu-command/src/formats/to/html.rs b/crates/nu-command/src/formats/to/html.rs index 5382558f9..ca689b799 100644 --- a/crates/nu-command/src/formats/to/html.rs +++ b/crates/nu-command/src/formats/to/html.rs @@ -5,7 +5,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; use rust_embed::RustEmbed; use serde::{Deserialize, Serialize}; @@ -90,6 +90,7 @@ impl Command for ToHtml { fn signature(&self) -> Signature { Signature::build("to html") + .input_output_types(vec![(Type::Any, Type::String)]) .switch("html-color", "change ansi colors to html colors", Some('c')) .switch("no-color", "remove all ansi colors in output", Some('n')) .switch( diff --git a/crates/nu-command/src/formats/to/json.rs b/crates/nu-command/src/formats/to/json.rs index d786db848..0ab96681c 100644 --- a/crates/nu-command/src/formats/to/json.rs +++ b/crates/nu-command/src/formats/to/json.rs @@ -2,7 +2,8 @@ use nu_engine::CallExt; use nu_protocol::ast::{Call, PathMember}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value, + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type, + Value, }; #[derive(Clone)] @@ -15,6 +16,7 @@ impl Command for ToJson { fn signature(&self) -> Signature { Signature::build("to json") + .input_output_types(vec![(Type::Any, Type::String)]) .switch("raw", "remove all of the whitespace", Some('r')) .named( "indent", diff --git a/crates/nu-command/src/formats/to/md.rs b/crates/nu-command/src/formats/to/md.rs index 44f5d155a..3035f9228 100644 --- a/crates/nu-command/src/formats/to/md.rs +++ b/crates/nu-command/src/formats/to/md.rs @@ -3,7 +3,8 @@ use indexmap::map::IndexMap; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value, + Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, + Value, }; #[derive(Clone)] @@ -16,6 +17,7 @@ impl Command for ToMd { fn signature(&self) -> Signature { Signature::build("to md") + .input_output_types(vec![(Type::Any, Type::String)]) .switch( "pretty", "Formats the Markdown table to vertically align items", diff --git a/crates/nu-command/src/formats/to/nuon.rs b/crates/nu-command/src/formats/to/nuon.rs index b57f2f8cd..99bcd3b71 100644 --- a/crates/nu-command/src/formats/to/nuon.rs +++ b/crates/nu-command/src/formats/to/nuon.rs @@ -4,7 +4,7 @@ use nu_parser::escape_quote_string; use nu_protocol::ast::{Call, RangeInclusion}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value, + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value, }; #[derive(Clone)] @@ -16,7 +16,9 @@ impl Command for ToNuon { } fn signature(&self) -> Signature { - Signature::build("to nuon").category(Category::Experimental) + Signature::build("to nuon") + .input_output_types(vec![(Type::Any, Type::String)]) + .category(Category::Experimental) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/formats/to/text.rs b/crates/nu-command/src/formats/to/text.rs index 9551ee156..cb7da20ce 100644 --- a/crates/nu-command/src/formats/to/text.rs +++ b/crates/nu-command/src/formats/to/text.rs @@ -3,7 +3,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, format_duration, format_filesize_from_conf, Category, Config, Example, IntoPipelineData, - PipelineData, ShellError, Signature, Value, + PipelineData, ShellError, Signature, Type, Value, }; #[derive(Clone)] @@ -15,7 +15,9 @@ impl Command for ToText { } fn signature(&self) -> Signature { - Signature::build("to text").category(Category::Formats) + Signature::build("to text") + .input_output_types(vec![(Type::Any, Type::String)]) + .category(Category::Formats) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/formats/to/toml.rs b/crates/nu-command/src/formats/to/toml.rs index 4fd22d887..1423d4817 100644 --- a/crates/nu-command/src/formats/to/toml.rs +++ b/crates/nu-command/src/formats/to/toml.rs @@ -13,7 +13,9 @@ impl Command for ToToml { } fn signature(&self) -> Signature { - Signature::build("to toml").category(Category::Formats) + Signature::build("to toml") + .input_output_types(vec![(Type::Any, Type::String)]) + .category(Category::Formats) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/formats/to/tsv.rs b/crates/nu-command/src/formats/to/tsv.rs index 8efca319e..a4b3f59b6 100644 --- a/crates/nu-command/src/formats/to/tsv.rs +++ b/crates/nu-command/src/formats/to/tsv.rs @@ -1,7 +1,9 @@ use crate::formats::to::delimited::to_delimited_data; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Config, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{ + Category, Config, Example, PipelineData, ShellError, Signature, Span, Type, Value, +}; #[derive(Clone)] pub struct ToTsv; @@ -13,6 +15,7 @@ impl Command for ToTsv { fn signature(&self) -> Signature { Signature::build("to tsv") + .input_output_types(vec![(Type::Any, Type::String)]) .switch( "noheaders", "do not output the column names as the first row", diff --git a/crates/nu-command/src/formats/to/url.rs b/crates/nu-command/src/formats/to/url.rs index 80aecfe8c..046ec6ae4 100644 --- a/crates/nu-command/src/formats/to/url.rs +++ b/crates/nu-command/src/formats/to/url.rs @@ -1,7 +1,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value, + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value, }; #[derive(Clone)] @@ -13,7 +13,9 @@ impl Command for ToUrl { } fn signature(&self) -> Signature { - Signature::build("to url").category(Category::Formats) + Signature::build("to url") + .input_output_types(vec![(Type::Table(vec![]), Type::String)]) + .category(Category::Formats) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/formats/to/xml.rs b/crates/nu-command/src/formats/to/xml.rs index c6fb7f8eb..13333e89d 100644 --- a/crates/nu-command/src/formats/to/xml.rs +++ b/crates/nu-command/src/formats/to/xml.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, - Spanned, SyntaxShape, Value, + Spanned, SyntaxShape, Type, Value, }; use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; use std::collections::HashSet; @@ -21,6 +21,7 @@ impl Command for ToXml { fn signature(&self) -> Signature { Signature::build("to xml") + .input_output_types(vec![(Type::Record(vec![]), Type::String)]) .named( "pretty", SyntaxShape::Int, diff --git a/crates/nu-command/src/formats/to/yaml.rs b/crates/nu-command/src/formats/to/yaml.rs index b28380518..2009a1442 100644 --- a/crates/nu-command/src/formats/to/yaml.rs +++ b/crates/nu-command/src/formats/to/yaml.rs @@ -1,7 +1,7 @@ use nu_protocol::ast::{Call, PathMember}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value, + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value, }; #[derive(Clone)] @@ -13,7 +13,9 @@ impl Command for ToYaml { } fn signature(&self) -> Signature { - Signature::build("to yaml").category(Category::Formats) + Signature::build("to yaml") + .input_output_types(vec![(Type::Any, Type::String)]) + .category(Category::Formats) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/generators/seq.rs b/crates/nu-command/src/generators/seq.rs index 3ec9c15f5..e455ac7ae 100644 --- a/crates/nu-command/src/generators/seq.rs +++ b/crates/nu-command/src/generators/seq.rs @@ -3,7 +3,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; use std::cmp; @@ -17,6 +17,11 @@ impl Command for Seq { fn signature(&self) -> Signature { Signature::build("seq") + .input_output_types(vec![ + (Type::Nothing, Type::List(Box::new(Type::Number))), + // -s flag + (Type::Nothing, Type::String), + ]) .rest("rest", SyntaxShape::Number, "sequence values") .named( "separator", diff --git a/crates/nu-command/src/generators/seq_char.rs b/crates/nu-command/src/generators/seq_char.rs index 15f11a75b..528615603 100644 --- a/crates/nu-command/src/generators/seq_char.rs +++ b/crates/nu-command/src/generators/seq_char.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ ast::Call, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, - Spanned, SyntaxShape, Value, + Spanned, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -19,6 +19,10 @@ impl Command for SeqChar { fn signature(&self) -> Signature { Signature::build("seq char") + .input_output_types(vec![ + (Type::Nothing, Type::List(Box::new(Type::Any))), + (Type::Nothing, Type::String), + ]) .rest("rest", SyntaxShape::String, "sequence chars") .named( "separator", diff --git a/crates/nu-command/src/generators/seq_date.rs b/crates/nu-command/src/generators/seq_date.rs index c3ed00bb2..e9bbb9739 100644 --- a/crates/nu-command/src/generators/seq_date.rs +++ b/crates/nu-command/src/generators/seq_date.rs @@ -5,7 +5,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -22,6 +22,7 @@ impl Command for SeqDate { fn signature(&self) -> nu_protocol::Signature { Signature::build("seq date") + .input_output_types(vec![(Type::Nothing, Type::List(Box::new(Type::String)))]) .named( "separator", SyntaxShape::String, diff --git a/crates/nu-command/src/hash/generic_digest.rs b/crates/nu-command/src/hash/generic_digest.rs index 6fada940a..212ee33fa 100644 --- a/crates/nu-command/src/hash/generic_digest.rs +++ b/crates/nu-command/src/hash/generic_digest.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::ast::{Call, CellPath}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Span; -use nu_protocol::{Example, PipelineData, ShellError, Signature, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value}; use std::marker::PhantomData; pub trait HashDigest: digest::Digest + Clone { @@ -50,6 +50,10 @@ where fn signature(&self) -> Signature { Signature::build(self.name()) + .input_output_types(vec![ + (Type::String, Type::String), + (Type::String, Type::Binary), + ]) .switch( "binary", "Output binary instead of hexadecimal representation", diff --git a/crates/nu-command/src/hash/md5.rs b/crates/nu-command/src/hash/md5.rs index a27083596..f5bd62915 100644 --- a/crates/nu-command/src/hash/md5.rs +++ b/crates/nu-command/src/hash/md5.rs @@ -12,7 +12,7 @@ impl HashDigest for Md5 { fn examples() -> Vec { vec![ Example { - description: "get a hexadecimaly encoded string of the md5 digest of a string", + description: "Return the md5 hash of a string, hex-encoded", example: "echo 'abcdefghijklmnopqrstuvwxyz' | hash md5", result: Some(Value::String { val: "c3fcd3d76192e4007dfb496cca67e13b".to_owned(), @@ -20,7 +20,7 @@ impl HashDigest for Md5 { }), }, Example { - description: "get the md5 digest of a string in binary", + description: "Return the md5 hash of a string, as binary", example: "echo 'abcdefghijklmnopqrstuvwxyz' | hash md5 --binary", result: Some(Value::Binary { val: vec![ @@ -31,7 +31,7 @@ impl HashDigest for Md5 { }), }, Example { - description: "md5 encode a file", + description: "Return the md5 hash of a file's contents", example: "open ./nu_0_24_1_windows.zip | hash md5", result: None, }, diff --git a/crates/nu-command/src/hash/sha256.rs b/crates/nu-command/src/hash/sha256.rs index 2852ef601..d70ab9638 100644 --- a/crates/nu-command/src/hash/sha256.rs +++ b/crates/nu-command/src/hash/sha256.rs @@ -12,7 +12,7 @@ impl HashDigest for Sha256 { fn examples() -> Vec { vec![ Example { - description: "get a hexadecimaly encoded string of the sha256 digest of a string", + description: "Return the sha256 hash of a string, hex-encoded", example: "echo 'abcdefghijklmnopqrstuvwxyz' | hash sha256", result: Some(Value::String { val: "71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73" @@ -21,7 +21,7 @@ impl HashDigest for Sha256 { }), }, Example { - description: "get the sha256 digest of a string in binary", + description: "Return the sha256 hash of a string, as binary", example: "echo 'abcdefghijklmnopqrstuvwxyz' | hash sha256 --binary", result: Some(Value::Binary { val: vec![ @@ -33,7 +33,7 @@ impl HashDigest for Sha256 { }), }, Example { - description: "sha256 encode a file", + description: "Return the sha256 hash of a file's contents", example: "open ./nu_0_24_1_windows.zip | hash sha256", result: None, }, diff --git a/crates/nu-command/src/math/abs.rs b/crates/nu-command/src/math/abs.rs index 9cf131ce4..c3deb8b0f 100644 --- a/crates/nu-command/src/math/abs.rs +++ b/crates/nu-command/src/math/abs.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -11,11 +11,14 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("math abs").category(Category::Math) + Signature::build("math abs") + .input_output_types(vec![(Type::Number, Type::Number)]) + .vectorizes_over_list(true) + .category(Category::Math) } fn usage(&self) -> &str { - "Returns absolute values of a list of numbers" + "Returns the absolute value of a number" } fn search_terms(&self) -> Vec<&str> { @@ -38,7 +41,7 @@ impl Command for SubCommand { fn examples(&self) -> Vec { vec![Example { - description: "Get absolute of each value in a list of numbers", + description: "Compute absolute value of each number in a list of numbers", example: "[-50 -100.0 25] | math abs", result: Some(Value::List { vals: vec![ diff --git a/crates/nu-command/src/math/avg.rs b/crates/nu-command/src/math/avg.rs index dac06913e..555f44eb0 100644 --- a/crates/nu-command/src/math/avg.rs +++ b/crates/nu-command/src/math/avg.rs @@ -2,7 +2,7 @@ use crate::math::reducers::{reducer_for, Reduce}; use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -13,11 +13,13 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("math avg").category(Category::Math) + Signature::build("math avg") + .input_output_types(vec![(Type::List(Box::new(Type::Number)), Type::Number)]) + .category(Category::Math) } fn usage(&self) -> &str { - "Finds the average of a list of numbers or tables" + "Returns the average of a list of numbers" } fn search_terms(&self) -> Vec<&str> { @@ -36,7 +38,7 @@ impl Command for SubCommand { fn examples(&self) -> Vec { vec![Example { - description: "Get the average of a list of numbers", + description: "Compute the average of a list of numbers", example: "[-50 100.0 25] | math avg", result: Some(Value::Float { val: 25.0, diff --git a/crates/nu-command/src/math/ceil.rs b/crates/nu-command/src/math/ceil.rs index 3aa6697e0..df13901ff 100644 --- a/crates/nu-command/src/math/ceil.rs +++ b/crates/nu-command/src/math/ceil.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -11,11 +11,14 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("math ceil").category(Category::Math) + Signature::build("math ceil") + .input_output_types(vec![(Type::Number, Type::Int)]) + .vectorizes_over_list(true) + .category(Category::Math) } fn usage(&self) -> &str { - "Applies the ceil function to a list of numbers" + "Returns the ceil of a number (smallest integer greater than or equal to that number)" } fn search_terms(&self) -> Vec<&str> { diff --git a/crates/nu-command/src/math/eval.rs b/crates/nu-command/src/math/eval.rs index 28bc02b56..38cec52fc 100644 --- a/crates/nu-command/src/math/eval.rs +++ b/crates/nu-command/src/math/eval.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -23,6 +23,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math eval") + .input_output_types(vec![(Type::String, Type::Number)]) .optional( "math expression", SyntaxShape::String, diff --git a/crates/nu-command/src/math/floor.rs b/crates/nu-command/src/math/floor.rs index cc7a4ad48..c718bb482 100644 --- a/crates/nu-command/src/math/floor.rs +++ b/crates/nu-command/src/math/floor.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -11,11 +11,14 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("math floor").category(Category::Math) + Signature::build("math floor") + .input_output_types(vec![(Type::Number, Type::Int)]) + .vectorizes_over_list(true) + .category(Category::Math) } fn usage(&self) -> &str { - "Applies the floor function to a list of numbers" + "Returns the floor of a number (largest integer less than or equal to that number)" } fn search_terms(&self) -> Vec<&str> { diff --git a/crates/nu-command/src/math/max.rs b/crates/nu-command/src/math/max.rs index afccd71f7..be71eb6de 100644 --- a/crates/nu-command/src/math/max.rs +++ b/crates/nu-command/src/math/max.rs @@ -2,7 +2,7 @@ use crate::math::reducers::{reducer_for, Reduce}; use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -13,11 +13,16 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("math max").category(Category::Math) + Signature::build("math max") + .input_output_types(vec![ + (Type::List(Box::new(Type::Number)), Type::Number), + (Type::Table(vec![]), Type::Record(vec![])), + ]) + .category(Category::Math) } fn usage(&self) -> &str { - "Finds the maximum within a list of numbers or tables" + "Returns the maximum of a list of numbers, or of columns in a table" } fn search_terms(&self) -> Vec<&str> { @@ -35,11 +40,22 @@ impl Command for SubCommand { } fn examples(&self) -> Vec { - vec![Example { - description: "Find the maximum of list of numbers", - example: "[-50 100 25] | math max", - result: Some(Value::test_int(100)), - }] + vec![ + Example { + description: "Find the maximum of list of numbers", + example: "[-50 100 25] | math max", + result: Some(Value::test_int(100)), + }, + Example { + description: "Find the maxima of the columns of a table", + example: "[{a: 1 b: 3} {a: 2 b: -1}] | math max", + result: Some(Value::Record { + cols: vec!["a".to_string(), "b".to_string()], + vals: vec![Value::test_int(2), Value::test_int(3)], + span: Span::test_data(), + }), + }, + ] } } diff --git a/crates/nu-command/src/math/median.rs b/crates/nu-command/src/math/median.rs index a9dfebe4a..ade21335c 100644 --- a/crates/nu-command/src/math/median.rs +++ b/crates/nu-command/src/math/median.rs @@ -4,7 +4,7 @@ use crate::math::avg::average; use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -15,11 +15,16 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("math median").category(Category::Math) + Signature::build("math median") + .input_output_types(vec![ + (Type::List(Box::new(Type::Number)), Type::Number), + (Type::Table(vec![]), Type::Record(vec![])), + ]) + .category(Category::Math) } fn usage(&self) -> &str { - "Gets the median of a list of numbers" + "Computes the median of a list of numbers" } fn search_terms(&self) -> Vec<&str> { @@ -37,14 +42,25 @@ impl Command for SubCommand { } fn examples(&self) -> Vec { - vec![Example { - description: "Get the median of a list of numbers", - example: "[3 8 9 12 12 15] | math median", - result: Some(Value::Float { - val: 10.5, - span: Span::test_data(), - }), - }] + vec![ + Example { + description: "Compute the median of a list of numbers", + example: "[3 8 9 12 12 15] | math median", + result: Some(Value::Float { + val: 10.5, + span: Span::test_data(), + }), + }, + Example { + description: "Compute the medians of the columns of a table", + example: "[{a: 1 b: 3} {a: 2 b: -1} {a: -3 b: 5}] | math median", + result: Some(Value::Record { + cols: vec!["a".to_string(), "b".to_string()], + vals: vec![Value::test_int(1), Value::test_int(3)], + span: Span::test_data(), + }), + }, + ] } } diff --git a/crates/nu-command/src/math/min.rs b/crates/nu-command/src/math/min.rs index 1b637d86d..d2f62a42e 100644 --- a/crates/nu-command/src/math/min.rs +++ b/crates/nu-command/src/math/min.rs @@ -2,7 +2,7 @@ use crate::math::reducers::{reducer_for, Reduce}; use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -13,7 +13,12 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("math min").category(Category::Math) + Signature::build("math min") + .input_output_types(vec![ + (Type::List(Box::new(Type::Number)), Type::Number), + (Type::Table(vec![]), Type::Record(vec![])), + ]) + .category(Category::Math) } fn usage(&self) -> &str { @@ -35,11 +40,22 @@ impl Command for SubCommand { } fn examples(&self) -> Vec { - vec![Example { - description: "Get the minimum of a list of numbers", - example: "[-50 100 25] | math min", - result: Some(Value::test_int(-50)), - }] + vec![ + Example { + description: "Compute the minimum of a list of numbers", + example: "[-50 100 25] | math min", + result: Some(Value::test_int(-50)), + }, + Example { + description: "Compute the minima of the columns of a table", + example: "[{a: 1 b: 3} {a: 2 b: -1}] | math min", + result: Some(Value::Record { + cols: vec!["a".to_string(), "b".to_string()], + vals: vec![Value::test_int(1), Value::test_int(-1)], + span: Span::test_data(), + }), + }, + ] } } diff --git a/crates/nu-command/src/math/mode.rs b/crates/nu-command/src/math/mode.rs index 2a57059c9..cf34c6978 100644 --- a/crates/nu-command/src/math/mode.rs +++ b/crates/nu-command/src/math/mode.rs @@ -1,7 +1,7 @@ use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; use std::cmp::Ordering; #[derive(Clone)] @@ -36,11 +36,19 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("math mode").category(Category::Math) + Signature::build("math mode") + .input_output_types(vec![ + ( + Type::List(Box::new(Type::Number)), + Type::List(Box::new(Type::Number)), + ), + (Type::Table(vec![]), Type::Record(vec![])), + ]) + .category(Category::Math) } fn usage(&self) -> &str { - "Gets the most frequent element(s) from a list of numbers or tables" + "Returns the most frequent element(s) from a list of numbers or tables" } fn search_terms(&self) -> Vec<&str> { @@ -58,14 +66,34 @@ impl Command for SubCommand { } fn examples(&self) -> Vec { - vec![Example { - description: "Get the mode(s) of a list of numbers", - example: "[3 3 9 12 12 15] | math mode", - result: Some(Value::List { - vals: vec![Value::test_int(3), Value::test_int(12)], - span: Span::test_data(), - }), - }] + vec![ + Example { + description: "Compute the mode(s) of a list of numbers", + example: "[3 3 9 12 12 15] | math mode", + result: Some(Value::List { + vals: vec![Value::test_int(3), Value::test_int(12)], + span: Span::test_data(), + }), + }, + Example { + description: "Compute the mode(s) of the columns of a table", + example: "[{a: 1 b: 3} {a: 2 b: -1} {a: 1 b: 5}] | math mode", + result: Some(Value::Record { + cols: vec!["a".to_string(), "b".to_string()], + vals: vec![ + Value::List { + vals: vec![Value::test_int(1)], + span: Span::test_data(), + }, + Value::List { + vals: vec![Value::test_int(-1), Value::test_int(3), Value::test_int(5)], + span: Span::test_data(), + }, + ], + span: Span::test_data(), + }), + }, + ] } } diff --git a/crates/nu-command/src/math/product.rs b/crates/nu-command/src/math/product.rs index 1fcfd197b..17071c2c1 100644 --- a/crates/nu-command/src/math/product.rs +++ b/crates/nu-command/src/math/product.rs @@ -2,7 +2,7 @@ use crate::math::reducers::{reducer_for, Reduce}; use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -13,11 +13,13 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("math product").category(Category::Math) + Signature::build("math product") + .input_output_types(vec![(Type::List(Box::new(Type::Number)), Type::Number)]) + .category(Category::Math) } fn usage(&self) -> &str { - "Finds the product of a list of numbers or tables" + "Returns the product of a list of numbers or the products of each column of a table" } fn search_terms(&self) -> Vec<&str> { @@ -36,7 +38,7 @@ impl Command for SubCommand { fn examples(&self) -> Vec { vec![Example { - description: "Get the product of a list of numbers", + description: "Compute the product of a list of numbers", example: "[2 3 3 4] | math product", result: Some(Value::test_int(72)), }] diff --git a/crates/nu-command/src/math/round.rs b/crates/nu-command/src/math/round.rs index 8f47e2425..739313bb4 100644 --- a/crates/nu-command/src/math/round.rs +++ b/crates/nu-command/src/math/round.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -15,6 +15,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math round") + .input_output_types(vec![(Type::Number, Type::Number)]) + .vectorizes_over_list(true) .named( "precision", SyntaxShape::Number, @@ -25,11 +27,11 @@ impl Command for SubCommand { } fn usage(&self) -> &str { - "Applies the round function to a list of numbers" + "Returns the input number rounded to the specified precision" } fn search_terms(&self) -> Vec<&str> { - vec!["approx", "rough"] + vec!["approx", "closest", "nearest"] } fn run( diff --git a/crates/nu-command/src/math/sqrt.rs b/crates/nu-command/src/math/sqrt.rs index 2a38a76dc..d04754fc1 100644 --- a/crates/nu-command/src/math/sqrt.rs +++ b/crates/nu-command/src/math/sqrt.rs @@ -1,6 +1,6 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -11,11 +11,14 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("math sqrt").category(Category::Math) + Signature::build("math sqrt") + .input_output_types(vec![(Type::Number, Type::Number)]) + .vectorizes_over_list(true) + .category(Category::Math) } fn usage(&self) -> &str { - "Applies the square root function to a list of numbers" + "Returns the square root of the input number" } fn search_terms(&self) -> Vec<&str> { @@ -38,7 +41,7 @@ impl Command for SubCommand { fn examples(&self) -> Vec { vec![Example { - description: "Apply the square root function to a list of numbers", + description: "Compute the square root of each number in a list", example: "[9 16] | math sqrt", result: Some(Value::List { vals: vec![Value::test_int(3), Value::test_int(4)], diff --git a/crates/nu-command/src/math/stddev.rs b/crates/nu-command/src/math/stddev.rs index cb76fd40c..544ffe64f 100644 --- a/crates/nu-command/src/math/stddev.rs +++ b/crates/nu-command/src/math/stddev.rs @@ -2,7 +2,7 @@ use super::variance::compute_variance as variance; use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -14,12 +14,17 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math stddev") - .switch("sample", "calculate sample standard deviation", Some('s')) + .input_output_types(vec![(Type::List(Box::new(Type::Number)), Type::Number)]) + .switch( + "sample", + "calculate sample standard deviation (i.e. using N-1 as the denominator)", + Some('s'), + ) .category(Category::Math) } fn usage(&self) -> &str { - "Finds the stddev of a list of numbers or tables" + "Returns the standard deviation of a list of numbers, or of each column in a table" } fn search_terms(&self) -> Vec<&str> { @@ -47,7 +52,7 @@ impl Command for SubCommand { fn examples(&self) -> Vec { vec![ Example { - description: "Get the stddev of a list of numbers", + description: "Compute the standard deviation of a list of numbers", example: "[1 2 3 4 5] | math stddev", result: Some(Value::Float { val: std::f64::consts::SQRT_2, @@ -55,7 +60,7 @@ impl Command for SubCommand { }), }, Example { - description: "Get the sample stddev of a list of numbers", + description: "Compute the sample standard deviation of a list of numbers", example: "[1 2 3 4 5] | math stddev -s", result: Some(Value::Float { val: 1.5811388300841898, diff --git a/crates/nu-command/src/math/sum.rs b/crates/nu-command/src/math/sum.rs index e341b099e..44138ab3a 100644 --- a/crates/nu-command/src/math/sum.rs +++ b/crates/nu-command/src/math/sum.rs @@ -2,7 +2,7 @@ use crate::math::reducers::{reducer_for, Reduce}; use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -13,15 +13,17 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("math sum").category(Category::Math) + Signature::build("math sum") + .input_output_types(vec![(Type::List(Box::new(Type::Number)), Type::Number)]) + .category(Category::Math) } fn usage(&self) -> &str { - "Finds the sum of a list of numbers or tables" + "Returns the sum of a list of numbers or of each column in a table" } fn search_terms(&self) -> Vec<&str> { - vec!["plus", "add", "+"] + vec!["plus", "add", "total", "+"] } fn run( diff --git a/crates/nu-command/src/math/variance.rs b/crates/nu-command/src/math/variance.rs index d70eba763..3a3a2859a 100644 --- a/crates/nu-command/src/math/variance.rs +++ b/crates/nu-command/src/math/variance.rs @@ -1,7 +1,7 @@ use crate::math::utils::run_with_function; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -13,12 +13,17 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("math variance") - .switch("sample", "calculate sample variance", Some('s')) + .input_output_types(vec![(Type::List(Box::new(Type::Number)), Type::Number)]) + .switch( + "sample", + "calculate sample variance (i.e. using N-1 as the denominator)", + Some('s'), + ) .category(Category::Math) } fn usage(&self) -> &str { - "Finds the variance of a list of numbers or tables" + "Returns the variance of a list of numbers or of each column in a table" } fn search_terms(&self) -> Vec<&str> { diff --git a/crates/nu-command/src/network/fetch.rs b/crates/nu-command/src/network/fetch.rs index 994f094ab..945247351 100644 --- a/crates/nu-command/src/network/fetch.rs +++ b/crates/nu-command/src/network/fetch.rs @@ -297,18 +297,18 @@ impl Command for SubCommand { fn examples(&self) -> Vec { vec![ Example { - description: "Fetch content from url.com", - example: "fetch url.com", + description: "Fetch content from example.com", + example: "fetch https://www.example.com", result: None, }, Example { - description: "Fetch content from url.com, with username and password", - example: "fetch -u myuser -p mypass url.com", + description: "Fetch content from example.com, with username and password", + example: "fetch -u myuser -p mypass https://www.example.com", result: None, }, Example { - description: "Fetch content from url.com, with custom header", - example: "fetch -H [my-header-key my-header-value] url.com", + description: "Fetch content from example.com, with custom header", + example: "fetch -H [my-header-key my-header-value] https://www.example.com", result: None, }, ] diff --git a/crates/nu-command/src/network/url/host.rs b/crates/nu-command/src/network/url/host.rs index 9072bdf32..cff0fc471 100644 --- a/crates/nu-command/src/network/url/host.rs +++ b/crates/nu-command/src/network/url/host.rs @@ -1,7 +1,7 @@ use super::{operator, url}; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -13,6 +13,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("url host") + .input_output_types(vec![(Type::String, Type::String)]) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/network/url/path.rs b/crates/nu-command/src/network/url/path.rs index a6bf7a000..0bdcfd0b9 100644 --- a/crates/nu-command/src/network/url/path.rs +++ b/crates/nu-command/src/network/url/path.rs @@ -1,7 +1,7 @@ use super::{operator, url}; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -13,6 +13,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("url path") + .input_output_types(vec![(Type::String, Type::String)]) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/network/url/query.rs b/crates/nu-command/src/network/url/query.rs index 195c1fafd..729c49503 100644 --- a/crates/nu-command/src/network/url/query.rs +++ b/crates/nu-command/src/network/url/query.rs @@ -1,7 +1,7 @@ use super::{operator, url}; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -13,6 +13,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("url query") + .input_output_types(vec![(Type::String, Type::String)]) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/network/url/scheme.rs b/crates/nu-command/src/network/url/scheme.rs index 23b4f42cf..9ecc9af71 100644 --- a/crates/nu-command/src/network/url/scheme.rs +++ b/crates/nu-command/src/network/url/scheme.rs @@ -1,7 +1,7 @@ use super::{operator, url}; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -13,6 +13,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("url scheme") + .input_output_types(vec![(Type::String, Type::String)]) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/network/url/url_.rs b/crates/nu-command/src/network/url/url_.rs index 52c481882..53dc12708 100644 --- a/crates/nu-command/src/network/url/url_.rs +++ b/crates/nu-command/src/network/url/url_.rs @@ -2,7 +2,7 @@ use nu_engine::get_full_help; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, IntoPipelineData, PipelineData, Signature, Value, + Category, IntoPipelineData, PipelineData, Signature, Type, Value, }; #[derive(Clone)] @@ -14,7 +14,9 @@ impl Command for Url { } fn signature(&self) -> Signature { - Signature::build("url").category(Category::Network) + Signature::build("url") + .input_output_types(vec![(Type::String, Type::String)]) + .category(Category::Network) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/path/basename.rs b/crates/nu-command/src/path/basename.rs index 223a86d5c..bab776af6 100644 --- a/crates/nu-command/src/path/basename.rs +++ b/crates/nu-command/src/path/basename.rs @@ -1,7 +1,7 @@ use std::path::Path; use nu_engine::CallExt; -use nu_protocol::{engine::Command, Example, Signature, Span, Spanned, SyntaxShape, Value}; +use nu_protocol::{engine::Command, Example, Signature, Span, Spanned, SyntaxShape, Type, Value}; use super::PathSubcommandArguments; @@ -26,6 +26,11 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("path basename") + .input_output_types(vec![ + (Type::String, Type::String), + // TODO: Why do these commands not use CellPaths in a standard way? + (Type::Table(vec![]), Type::Table(vec![])), + ]) .named( "columns", SyntaxShape::Table, diff --git a/crates/nu-command/src/path/dirname.rs b/crates/nu-command/src/path/dirname.rs index 061fa0eb0..6fbd170e6 100644 --- a/crates/nu-command/src/path/dirname.rs +++ b/crates/nu-command/src/path/dirname.rs @@ -1,7 +1,7 @@ use std::path::Path; use nu_engine::CallExt; -use nu_protocol::{engine::Command, Example, Signature, Span, Spanned, SyntaxShape, Value}; +use nu_protocol::{engine::Command, Example, Signature, Span, Spanned, SyntaxShape, Type, Value}; use super::PathSubcommandArguments; @@ -27,6 +27,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("path dirname") + .input_output_types(vec![(Type::String, Type::String)]) .named( "columns", SyntaxShape::Table, diff --git a/crates/nu-command/src/path/exists.rs b/crates/nu-command/src/path/exists.rs index e5dab5ef6..e00a6c2f3 100644 --- a/crates/nu-command/src/path/exists.rs +++ b/crates/nu-command/src/path/exists.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use nu_engine::{current_dir, CallExt}; use nu_path::expand_path_with; -use nu_protocol::{engine::Command, Example, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{engine::Command, Example, Signature, Span, SyntaxShape, Type, Value}; use super::PathSubcommandArguments; @@ -26,12 +26,14 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("path exists").named( - "columns", - SyntaxShape::Table, - "Optionally operate by column path", - Some('c'), - ) + Signature::build("path exists") + .input_output_types(vec![(Type::String, Type::Bool)]) + .named( + "columns", + SyntaxShape::Table, + "Optionally operate by column path", + Some('c'), + ) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/path/expand.rs b/crates/nu-command/src/path/expand.rs index 0f11d0477..cece7a4d3 100644 --- a/crates/nu-command/src/path/expand.rs +++ b/crates/nu-command/src/path/expand.rs @@ -3,7 +3,9 @@ use std::path::Path; use nu_engine::env::current_dir_str; use nu_engine::CallExt; use nu_path::{canonicalize_with, expand_path_with}; -use nu_protocol::{engine::Command, Example, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{ + engine::Command, Example, ShellError, Signature, Span, SyntaxShape, Type, Value, +}; use super::PathSubcommandArguments; @@ -30,6 +32,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("path expand") + .input_output_types(vec![(Type::String, Type::String)]) .switch( "strict", "Throw an error if the path could not be expanded", diff --git a/crates/nu-command/src/path/join.rs b/crates/nu-command/src/path/join.rs index 32e88e084..2625f4a27 100644 --- a/crates/nu-command/src/path/join.rs +++ b/crates/nu-command/src/path/join.rs @@ -6,7 +6,7 @@ use std::{ use nu_engine::CallExt; use nu_protocol::{ engine::Command, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, - Value, + Type, Value, }; use super::PathSubcommandArguments; @@ -32,6 +32,11 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("path join") + .input_output_types(vec![ + (Type::String, Type::String), + (Type::List(Box::new(Type::String)), Type::String), + (Type::Table(vec![]), Type::List(Box::new(Type::String))), + ]) .named( "columns", SyntaxShape::Table, diff --git a/crates/nu-command/src/path/parse.rs b/crates/nu-command/src/path/parse.rs index 14b9b64db..97aa6af5d 100644 --- a/crates/nu-command/src/path/parse.rs +++ b/crates/nu-command/src/path/parse.rs @@ -3,7 +3,7 @@ use std::path::Path; use indexmap::IndexMap; use nu_engine::CallExt; use nu_protocol::{ - engine::Command, Example, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + engine::Command, Example, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; use super::PathSubcommandArguments; @@ -29,6 +29,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("path parse") + .input_output_types(vec![(Type::String, Type::Record(vec![]))]) .named( "columns", SyntaxShape::Table, diff --git a/crates/nu-command/src/path/relative_to.rs b/crates/nu-command/src/path/relative_to.rs index 670d3ea7c..b40b61f2e 100644 --- a/crates/nu-command/src/path/relative_to.rs +++ b/crates/nu-command/src/path/relative_to.rs @@ -3,7 +3,7 @@ use std::path::Path; use nu_engine::CallExt; use nu_path::expand_to_real_path; use nu_protocol::{ - engine::Command, Example, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + engine::Command, Example, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; use super::PathSubcommandArguments; @@ -29,6 +29,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("path relative-to") + .input_output_types(vec![(Type::String, Type::String)]) .required( "path", SyntaxShape::String, diff --git a/crates/nu-command/src/path/split.rs b/crates/nu-command/src/path/split.rs index 173c3bc85..cefb0f4d7 100644 --- a/crates/nu-command/src/path/split.rs +++ b/crates/nu-command/src/path/split.rs @@ -1,7 +1,9 @@ use std::path::{Component, Path}; use nu_engine::CallExt; -use nu_protocol::{engine::Command, Example, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{ + engine::Command, Example, ShellError, Signature, Span, SyntaxShape, Type, Value, +}; use super::PathSubcommandArguments; @@ -24,12 +26,14 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("path split").named( - "columns", - SyntaxShape::Table, - "Optionally operate by column path", - Some('c'), - ) + Signature::build("path split") + .input_output_types(vec![(Type::String, Type::List(Box::new(Type::String)))]) + .named( + "columns", + SyntaxShape::Table, + "Optionally operate by column path", + Some('c'), + ) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/path/type.rs b/crates/nu-command/src/path/type.rs index 2a5689a1b..d14252567 100644 --- a/crates/nu-command/src/path/type.rs +++ b/crates/nu-command/src/path/type.rs @@ -1,7 +1,9 @@ use std::path::Path; use nu_engine::CallExt; -use nu_protocol::{engine::Command, Example, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{ + engine::Command, Example, ShellError, Signature, Span, SyntaxShape, Type, Value, +}; use super::PathSubcommandArguments; @@ -24,12 +26,14 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("path type").named( - "columns", - SyntaxShape::Table, - "Optionally operate by column path", - Some('c'), - ) + Signature::build("path type") + .input_output_types(vec![(Type::String, Type::String)]) + .named( + "columns", + SyntaxShape::Table, + "Optionally operate by column path", + Some('c'), + ) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/platform/ansi/ansi_.rs b/crates/nu-command/src/platform/ansi/ansi_.rs index 6750c289a..f20e62027 100644 --- a/crates/nu-command/src/platform/ansi/ansi_.rs +++ b/crates/nu-command/src/platform/ansi/ansi_.rs @@ -3,7 +3,7 @@ use nu_ansi_term::*; use nu_engine::CallExt; use nu_protocol::{ ast::Call, engine::Command, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, - PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use std::collections::HashMap; @@ -202,6 +202,7 @@ impl Command for AnsiCommand { fn signature(&self) -> Signature { Signature::build("ansi") + .input_output_types(vec![(Type::Nothing, Type::String)]) .optional( "code", SyntaxShape::Any, @@ -293,9 +294,13 @@ Format: # Example { description: "Use ansi to color text (italic bright yellow on red 'Hello' with green bold 'Nu' and purple bold 'World')", example: r#"[(ansi -e '3;93;41m') Hello (ansi reset) " " (ansi gb) Nu " " (ansi pb) World (ansi reset)] | str join"#, - result: Some(Value::test_string( - "\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld\u{1b}[0m", - )), + result: None, + // Test disabled because the final expression in the pipeline is + // not the command being tested, and this violated assumptions + // made by the run-time input/output type-checking tests. + // result: Some(Value::test_string( + // "\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld\u{1b}[0m", + // )), }, Example { description: "Use ansi to color text with a style (blue on red in bold)", diff --git a/crates/nu-command/src/platform/ansi/strip.rs b/crates/nu-command/src/platform/ansi/strip.rs index 8aa11d2d1..e18f4fe83 100644 --- a/crates/nu-command/src/platform/ansi/strip.rs +++ b/crates/nu-command/src/platform/ansi/strip.rs @@ -1,7 +1,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::Call, ast::CellPath, engine::Command, engine::EngineState, engine::Stack, Category, - Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -14,6 +14,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("ansi strip") + .input_output_types(vec![(Type::String, Type::String)]) .rest( "column path", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/platform/sleep.rs b/crates/nu-command/src/platform/sleep.rs index 4d883ab76..da24a67ae 100644 --- a/crates/nu-command/src/platform/sleep.rs +++ b/crates/nu-command/src/platform/sleep.rs @@ -2,7 +2,8 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value, + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, + Type, Value, }; use std::{ sync::atomic::Ordering, @@ -26,6 +27,7 @@ impl Command for Sleep { fn signature(&self) -> Signature { Signature::build("sleep") + .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .required("duration", SyntaxShape::Duration, "time to sleep") .rest("rest", SyntaxShape::Duration, "additional time") .category(Category::Platform) @@ -75,18 +77,20 @@ impl Command for Sleep { Example { description: "Sleep for 1sec", example: "sleep 1sec", - result: None, - }, - Example { - description: "Sleep for 3sec", - example: "sleep 1sec 1sec 1sec", - result: None, - }, - Example { - description: "Send output after 1sec", - example: "sleep 1sec; echo done", - result: Some(Value::test_string("done")), + result: Some(Value::Nothing { + span: Span::test_data(), + }), }, + // Example { + // description: "Sleep for 3sec", + // example: "sleep 1sec 1sec 1sec", + // result: None, + // }, + // Example { + // description: "Send output after 1sec", + // example: "sleep 1sec; echo done", + // result: None, + // }, ] } } diff --git a/crates/nu-command/src/strings/build_string.rs b/crates/nu-command/src/strings/build_string.rs index b1ba80824..085b9a351 100644 --- a/crates/nu-command/src/strings/build_string.rs +++ b/crates/nu-command/src/strings/build_string.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; #[derive(Clone)] @@ -25,6 +25,7 @@ impl Command for BuildString { fn signature(&self) -> nu_protocol::Signature { Signature::build("build-string") .rest("rest", SyntaxShape::String, "list of string") + .input_output_types(vec![(Type::Nothing, Type::String)]) .category(Category::Strings) } diff --git a/crates/nu-command/src/strings/char_.rs b/crates/nu-command/src/strings/char_.rs index a56417a26..be3b6944f 100644 --- a/crates/nu-command/src/strings/char_.rs +++ b/crates/nu-command/src/strings/char_.rs @@ -4,7 +4,7 @@ use lazy_static::lazy_static; use nu_engine::CallExt; use nu_protocol::{ ast::Call, engine::Command, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, - PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; // Character used to separate directories in a Path Environment variable on windows is ";" @@ -156,6 +156,7 @@ impl Command for Char { fn signature(&self) -> Signature { Signature::build("char") + .input_output_types(vec![(Type::Nothing, Type::String)]) .optional( "character", SyntaxShape::Any, diff --git a/crates/nu-command/src/strings/detect_columns.rs b/crates/nu-command/src/strings/detect_columns.rs index 05ec5c9cd..84230cc01 100644 --- a/crates/nu-command/src/strings/detect_columns.rs +++ b/crates/nu-command/src/strings/detect_columns.rs @@ -6,7 +6,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, - Spanned, SyntaxShape, Value, + Spanned, SyntaxShape, Type, Value, }; type Input<'t> = Peekable>; @@ -27,6 +27,7 @@ impl Command for DetectColumns { "number of rows to skip before detecting", Some('s'), ) + .input_output_types(vec![(Type::String, Type::Table(vec![]))]) .switch("no-headers", "don't detect headers", Some('n')) .category(Category::Strings) } diff --git a/crates/nu-command/src/strings/encode_decode/decode.rs b/crates/nu-command/src/strings/encode_decode/decode.rs index 410108a91..dea218588 100644 --- a/crates/nu-command/src/strings/encode_decode/decode.rs +++ b/crates/nu-command/src/strings/encode_decode/decode.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -24,6 +24,7 @@ impl Command for Decode { fn signature(&self) -> nu_protocol::Signature { Signature::build("decode") + .input_output_types(vec![(Type::Binary, Type::String)]) .required("encoding", SyntaxShape::String, "the text encoding to use") .category(Category::Strings) } diff --git a/crates/nu-command/src/strings/encode_decode/decode_base64.rs b/crates/nu-command/src/strings/encode_decode/decode_base64.rs index 3fbf39d5d..d92989b14 100644 --- a/crates/nu-command/src/strings/encode_decode/decode_base64.rs +++ b/crates/nu-command/src/strings/encode_decode/decode_base64.rs @@ -2,7 +2,7 @@ use super::base64::{operate, ActionType, CHARACTER_SET_DESC}; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -15,6 +15,11 @@ impl Command for DecodeBase64 { fn signature(&self) -> Signature { Signature::build("decode base64") + .input_output_types(vec![ + (Type::String, Type::String), + (Type::String, Type::Binary), + ]) + .vectorizes_over_list(true) .named( "character-set", SyntaxShape::String, diff --git a/crates/nu-command/src/strings/encode_decode/encode.rs b/crates/nu-command/src/strings/encode_decode/encode.rs index e22236186..c112c7a55 100644 --- a/crates/nu-command/src/strings/encode_decode/encode.rs +++ b/crates/nu-command/src/strings/encode_decode/encode.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -24,6 +24,7 @@ impl Command for Encode { fn signature(&self) -> nu_protocol::Signature { Signature::build("encode") + .input_output_types(vec![(Type::String, Type::Binary)]) .required("encoding", SyntaxShape::String, "the text encoding to use") .category(Category::Strings) } diff --git a/crates/nu-command/src/strings/encode_decode/encode_base64.rs b/crates/nu-command/src/strings/encode_decode/encode_base64.rs index 9b94a7224..44474ca93 100644 --- a/crates/nu-command/src/strings/encode_decode/encode_base64.rs +++ b/crates/nu-command/src/strings/encode_decode/encode_base64.rs @@ -15,6 +15,8 @@ impl Command for EncodeBase64 { fn signature(&self) -> Signature { Signature::build("encode base64") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .named( "character-set", SyntaxShape::String, diff --git a/crates/nu-command/src/strings/format/command.rs b/crates/nu-command/src/strings/format/command.rs index df72c095f..2d88988ff 100644 --- a/crates/nu-command/src/strings/format/command.rs +++ b/crates/nu-command/src/strings/format/command.rs @@ -2,9 +2,9 @@ use nu_engine::{eval_expression, CallExt}; use nu_parser::parse_expression; use nu_protocol::ast::{Call, PathMember}; use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet}; -use nu_protocol::Type; use nu_protocol::{ - Category, Example, ListStream, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, ListStream, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, + Value, }; #[derive(Clone)] @@ -17,6 +17,10 @@ impl Command for Format { fn signature(&self) -> Signature { Signature::build("format") + .input_output_types(vec![( + Type::Table(vec![]), + Type::List(Box::new(Type::String)), + )]) .required( "pattern", SyntaxShape::String, diff --git a/crates/nu-command/src/strings/format/filesize.rs b/crates/nu-command/src/strings/format/filesize.rs index 26081d292..71e52e6a4 100644 --- a/crates/nu-command/src/strings/format/filesize.rs +++ b/crates/nu-command/src/strings/format/filesize.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::{Call, CellPath}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ format_filesize, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; struct Arguments { @@ -28,6 +28,7 @@ impl Command for FileSize { fn signature(&self) -> Signature { Signature::build("format filesize") + .input_output_types(vec![(Type::Filesize, Type::String)]) .required( "format value", SyntaxShape::String, @@ -36,7 +37,7 @@ impl Command for FileSize { .rest( "rest", SyntaxShape::CellPath, - "optinally find and replace text by column paths", + "Optionally find and replace text by column paths", ) .category(Category::Strings) } @@ -78,19 +79,19 @@ impl Command for FileSize { fn examples(&self) -> Vec { vec![ Example { - description: "Convert the size row to KB", + description: "Convert the size column to KB", example: "ls | format filesize KB size", result: None, }, Example { - description: "Convert the apparent row to B", + description: "Convert the apparent column to B", example: "du | format filesize B apparent", result: None, }, Example { description: "Convert the size data to MB", example: "4Gb | format filesize MB", - result: None, + result: Some(Value::test_string("4000.0 MB")), }, ] } diff --git a/crates/nu-command/src/strings/parse.rs b/crates/nu-command/src/strings/parse.rs index 077837fbb..24ed13f19 100644 --- a/crates/nu-command/src/strings/parse.rs +++ b/crates/nu-command/src/strings/parse.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, ListStream, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, - Value, + Type, Value, }; #[derive(Clone)] @@ -30,6 +30,7 @@ impl Command for Parse { SyntaxShape::String, "the pattern to match. Eg) \"{foo}: {bar}\"", ) + .input_output_types(vec![(Type::String, Type::Table(vec![]))]) .switch("regex", "use full regex syntax for patterns", Some('r')) .category(Category::Strings) } diff --git a/crates/nu-command/src/strings/size.rs b/crates/nu-command/src/strings/size.rs index 16e288d26..0069e233c 100644 --- a/crates/nu-command/src/strings/size.rs +++ b/crates/nu-command/src/strings/size.rs @@ -1,7 +1,7 @@ use fancy_regex::Regex; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value}; use std::collections::BTreeMap; use std::{fmt, str}; use unicode_segmentation::UnicodeSegmentation; @@ -18,7 +18,9 @@ impl Command for Size { } fn signature(&self) -> Signature { - Signature::build("size").category(Category::Strings) + Signature::build("size") + .category(Category::Strings) + .input_output_types(vec![(Type::String, Type::Record(vec![]))]) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/strings/split/chars.rs b/crates/nu-command/src/strings/split/chars.rs index ef2822998..e2f82abe8 100644 --- a/crates/nu-command/src/strings/split/chars.rs +++ b/crates/nu-command/src/strings/split/chars.rs @@ -1,7 +1,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Type, Value, }; #[derive(Clone)] @@ -13,11 +13,14 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("split chars").category(Category::Strings) + Signature::build("split chars") + .input_output_types(vec![(Type::String, Type::List(Box::new(Type::String)))]) + .vectorizes_over_list(true) + .category(Category::Strings) } fn usage(&self) -> &str { - "Split a string's characters into separate rows" + "Split a string into a list of characters" } fn search_terms(&self) -> Vec<&str> { @@ -26,7 +29,7 @@ impl Command for SubCommand { fn examples(&self) -> Vec { vec![Example { - description: "Split the string's characters into separate rows", + description: "Split the string into a list of characters", example: "'hello' | split chars", result: Some(Value::List { vals: vec![ diff --git a/crates/nu-command/src/strings/split/column.rs b/crates/nu-command/src/strings/split/column.rs index 0cd8b78ea..1038bcfc3 100644 --- a/crates/nu-command/src/strings/split/column.rs +++ b/crates/nu-command/src/strings/split/column.rs @@ -2,7 +2,8 @@ use nu_engine::CallExt; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, + Value, }; #[derive(Clone)] @@ -15,6 +16,14 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("split column") + .input_output_types(vec![ + (Type::String, Type::Table(vec![])), + ( + // TODO: no test coverage (is this behavior a bug or a feature?) + Type::List(Box::new(Type::String)), + Type::Table(vec![]), + ), + ]) .required( "separator", SyntaxShape::String, @@ -71,7 +80,7 @@ impl Command for SubCommand { }, Example { description: "Split a string into columns of char and remove the empty columns", - example: "echo 'abc' | split column -c ''", + example: "'abc' | split column -c ''", result: Some(Value::List { vals: vec![Value::Record { cols: vec![ @@ -89,6 +98,25 @@ impl Command for SubCommand { span: Span::test_data(), }), }, + Example { + description: "Split a list of strings into a table", + example: "['a-b' 'c-d'] | split column -", + result: Some(Value::List { + vals: vec![ + Value::Record { + cols: vec!["column1".to_string(), "column2".to_string()], + vals: vec![Value::test_string("a"), Value::test_string("b")], + span: Span::test_data(), + }, + Value::Record { + cols: vec!["column1".to_string(), "column2".to_string()], + vals: vec![Value::test_string("c"), Value::test_string("d")], + span: Span::test_data(), + }, + ], + span: Span::test_data(), + }), + }, ] } } @@ -161,15 +189,14 @@ fn split_column_helper( } } -// #[cfg(test)] -// mod tests { -// use super::ShellError; -// use super::SubCommand; +#[cfg(test)] +mod test { + use super::*; -// #[test] -// fn examples_work_as_expected() -> Result<(), ShellError> { -// use crate::examples::test as test_examples; + #[test] + fn test_examples() { + use crate::test_examples; -// test_examples(SubCommand {}) -// } -// } + test_examples(SubCommand {}) + } +} diff --git a/crates/nu-command/src/strings/split/list.rs b/crates/nu-command/src/strings/split/list.rs index a18fd5ac7..0e84b43c9 100644 --- a/crates/nu-command/src/strings/split/list.rs +++ b/crates/nu-command/src/strings/split/list.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Example, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Value, + Category, Example, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -15,6 +15,10 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("split list") + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::List(Box::new(Type::Any)))), + )]) .required( "separator", SyntaxShape::Any, @@ -128,3 +132,15 @@ fn split_list( } .into_pipeline_data()) } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(SubCommand {}) + } +} diff --git a/crates/nu-command/src/strings/split/row.rs b/crates/nu-command/src/strings/split/row.rs index c19a8b683..29838ce38 100644 --- a/crates/nu-command/src/strings/split/row.rs +++ b/crates/nu-command/src/strings/split/row.rs @@ -2,7 +2,8 @@ use nu_engine::CallExt; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, + Value, }; #[derive(Clone)] @@ -15,6 +16,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("split row") + .input_output_types(vec![(Type::String, Type::List(Box::new(Type::String)))]) + .vectorizes_over_list(true) .required( "separator", SyntaxShape::String, @@ -133,15 +136,14 @@ fn split_row_helper( } } -// #[cfg(test)] -// mod tests { -// use super::ShellError; -// use super::SubCommand; +#[cfg(test)] +mod test { + use super::*; -// #[test] -// fn examples_work_as_expected() -> Result<(), ShellError> { -// use crate::examples::test as test_examples; + #[test] + fn test_examples() { + use crate::test_examples; -// test_examples(SubCommand {}) -// } -// } + test_examples(SubCommand {}) + } +} diff --git a/crates/nu-command/src/strings/split/words.rs b/crates/nu-command/src/strings/split/words.rs index 5012dc673..6439ba9bb 100644 --- a/crates/nu-command/src/strings/split/words.rs +++ b/crates/nu-command/src/strings/split/words.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -16,6 +16,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("split words") + .input_output_types(vec![(Type::String, Type::List(Box::new(Type::String)))]) + .vectorizes_over_list(true) .category(Category::Strings) // .switch( // "ignore-hyphenated", 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 162810655..217f5b17f 100644 --- a/crates/nu-command/src/strings/str_/case/camel_case.rs +++ b/crates/nu-command/src/strings/str_/case/camel_case.rs @@ -2,7 +2,7 @@ use inflector::cases::camelcase::to_camel_case; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use crate::operate; @@ -17,6 +17,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str camel-case") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/strings/str_/case/capitalize.rs b/crates/nu-command/src/strings/str_/case/capitalize.rs index 88ddb00e7..d0506bfe8 100644 --- a/crates/nu-command/src/strings/str_/case/capitalize.rs +++ b/crates/nu-command/src/strings/str_/case/capitalize.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -15,6 +15,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str capitalize") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/strings/str_/case/downcase.rs b/crates/nu-command/src/strings/str_/case/downcase.rs index 3ec0c9c7a..6307baa34 100644 --- a/crates/nu-command/src/strings/str_/case/downcase.rs +++ b/crates/nu-command/src/strings/str_/case/downcase.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -15,6 +15,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str downcase") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .rest( "rest", SyntaxShape::CellPath, 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 475ecbd83..83d96aa0b 100644 --- a/crates/nu-command/src/strings/str_/case/kebab_case.rs +++ b/crates/nu-command/src/strings/str_/case/kebab_case.rs @@ -2,7 +2,7 @@ use inflector::cases::kebabcase::to_kebab_case; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use crate::operate; @@ -17,6 +17,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str kebab-case") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .rest( "rest", SyntaxShape::CellPath, 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 464dd0a44..9256927a8 100644 --- a/crates/nu-command/src/strings/str_/case/pascal_case.rs +++ b/crates/nu-command/src/strings/str_/case/pascal_case.rs @@ -2,7 +2,7 @@ use inflector::cases::pascalcase::to_pascal_case; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use crate::operate; @@ -17,6 +17,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str pascal-case") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .rest( "rest", SyntaxShape::CellPath, 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 af2587878..32b88ed3d 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 @@ -2,7 +2,7 @@ use inflector::cases::screamingsnakecase::to_screaming_snake_case; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use crate::operate; @@ -16,6 +16,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str screaming-snake-case") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .rest( "rest", SyntaxShape::CellPath, 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 045979093..34f878178 100644 --- a/crates/nu-command/src/strings/str_/case/snake_case.rs +++ b/crates/nu-command/src/strings/str_/case/snake_case.rs @@ -2,7 +2,7 @@ use inflector::cases::snakecase::to_snake_case; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use crate::operate; @@ -16,6 +16,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str snake-case") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .rest( "rest", SyntaxShape::CellPath, 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 39b574b80..61ba3cdeb 100644 --- a/crates/nu-command/src/strings/str_/case/title_case.rs +++ b/crates/nu-command/src/strings/str_/case/title_case.rs @@ -2,7 +2,7 @@ use inflector::cases::titlecase::to_title_case; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; use crate::operate; @@ -17,6 +17,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str title-case") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/strings/str_/case/upcase.rs b/crates/nu-command/src/strings/str_/case/upcase.rs index 555783adc..63b60c1f6 100644 --- a/crates/nu-command/src/strings/str_/case/upcase.rs +++ b/crates/nu-command/src/strings/str_/case/upcase.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -13,11 +13,14 @@ impl Command for SubCommand { } fn signature(&self) -> Signature { - Signature::build("str upcase").rest( - "rest", - SyntaxShape::CellPath, - "optionally upcase text by column paths", - ) + Signature::build("str upcase") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) + .rest( + "rest", + SyntaxShape::CellPath, + "optionally upcase text by column paths", + ) } fn usage(&self) -> &str { diff --git a/crates/nu-command/src/strings/str_/collect.rs b/crates/nu-command/src/strings/str_/collect.rs index 8bedc748a..f3ff8efa7 100644 --- a/crates/nu-command/src/strings/str_/collect.rs +++ b/crates/nu-command/src/strings/str_/collect.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; #[derive(Clone)] @@ -16,6 +16,7 @@ impl Command for StrCollect { fn signature(&self) -> Signature { Signature::build("str collect") + .input_output_types(vec![(Type::List(Box::new(Type::String)), Type::String)]) .optional( "separator", SyntaxShape::String, diff --git a/crates/nu-command/src/strings/str_/contains.rs b/crates/nu-command/src/strings/str_/contains.rs index be972c7dd..1da80c5dd 100644 --- a/crates/nu-command/src/strings/str_/contains.rs +++ b/crates/nu-command/src/strings/str_/contains.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -29,6 +29,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str contains") + .input_output_types(vec![(Type::String, Type::Bool)]) + .vectorizes_over_list(true) .required("string", SyntaxShape::String, "the string to find") .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/distance.rs b/crates/nu-command/src/strings/str_/distance.rs index 87075a67e..c9ec679db 100644 --- a/crates/nu-command/src/strings/str_/distance.rs +++ b/crates/nu-command/src/strings/str_/distance.rs @@ -4,7 +4,7 @@ use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, levenshtein_distance, Category, Example, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -28,6 +28,7 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str distance") + .input_output_types(vec![(Type::String, Type::Int)]) .required( "compare-string", SyntaxShape::String, @@ -42,11 +43,11 @@ impl Command for SubCommand { } fn usage(&self) -> &str { - "compare two strings and return the edit distance/levenshtein distance" + "Compare two strings and return the edit distance/Levenshtein distance" } fn search_terms(&self) -> Vec<&str> { - vec!["edit", "match", "score", "levenshtein"] + vec!["edit", "levenshtein"] } fn run( @@ -70,8 +71,19 @@ impl Command for SubCommand { vec![Example { description: "get the edit distance between two strings", example: "'nushell' | str distance 'nutshell'", - result: Some(Value::Int { - val: 1, + result: Some(Value::test_int(1)), + }, + Example { + description: "Compute edit distance between strings in record and another string, using cell paths", + example: "[{a: 'nutshell' b: 'numetal'}] | str distance 'nushell' 'a' 'b'", + result: Some(Value::List { + vals: vec![ + Value::Record { + cols: vec!["a".to_string(), "b".to_string()], + vals: vec![Value::test_int(1), Value::test_int(4)], + span: Span::test_data(), + } + ], span: Span::test_data(), }), }] diff --git a/crates/nu-command/src/strings/str_/ends_with.rs b/crates/nu-command/src/strings/str_/ends_with.rs index cf38b5a12..dedb3aae3 100644 --- a/crates/nu-command/src/strings/str_/ends_with.rs +++ b/crates/nu-command/src/strings/str_/ends_with.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; struct Arguments { substring: String, @@ -27,11 +27,13 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str ends-with") + .input_output_types(vec![(Type::String, Type::Bool)]) + .vectorizes_over_list(true) .required("string", SyntaxShape::String, "the string to match") .rest( "rest", SyntaxShape::CellPath, - "optionally matches suffix of text by column paths", + "optionally matches suffix of text by cell paths", ) .category(Category::Strings) } diff --git a/crates/nu-command/src/strings/str_/index_of.rs b/crates/nu-command/src/strings/str_/index_of.rs index adfb003ed..e10953fd1 100644 --- a/crates/nu-command/src/strings/str_/index_of.rs +++ b/crates/nu-command/src/strings/str_/index_of.rs @@ -5,7 +5,7 @@ use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; use nu_protocol::Spanned; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; struct Arguments { end: bool, @@ -33,6 +33,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str index-of") + .input_output_types(vec![(Type::String, Type::Int)]) + .vectorizes_over_list(true) // TODO: no test coverage .required("string", SyntaxShape::String, "the string to find index of") .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/join.rs b/crates/nu-command/src/strings/str_/join.rs index fe8f65951..350b2e6b1 100644 --- a/crates/nu-command/src/strings/str_/join.rs +++ b/crates/nu-command/src/strings/str_/join.rs @@ -3,7 +3,7 @@ use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, - Value, + Type, Value, }; #[derive(Clone)] @@ -16,6 +16,7 @@ impl Command for StrJoin { fn signature(&self) -> Signature { Signature::build("str join") + .input_output_types(vec![(Type::List(Box::new(Type::String)), Type::String)]) .optional( "separator", SyntaxShape::String, diff --git a/crates/nu-command/src/strings/str_/length.rs b/crates/nu-command/src/strings/str_/length.rs index 77c4afb30..22cc71539 100644 --- a/crates/nu-command/src/strings/str_/length.rs +++ b/crates/nu-command/src/strings/str_/length.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -15,6 +15,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str length") + .input_output_types(vec![(Type::String, Type::Int)]) + .vectorizes_over_list(true) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/strings/str_/lpad.rs b/crates/nu-command/src/strings/str_/lpad.rs index 386dc6053..c22221e8e 100644 --- a/crates/nu-command/src/strings/str_/lpad.rs +++ b/crates/nu-command/src/strings/str_/lpad.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; struct Arguments { length: Option, @@ -28,6 +28,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str lpad") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .required_named("length", SyntaxShape::Int, "length to pad to", Some('l')) .required_named( "character", diff --git a/crates/nu-command/src/strings/str_/replace.rs b/crates/nu-command/src/strings/str_/replace.rs index eb594a6d1..60edfe3ab 100644 --- a/crates/nu-command/src/strings/str_/replace.rs +++ b/crates/nu-command/src/strings/str_/replace.rs @@ -4,7 +4,8 @@ use nu_engine::CallExt; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, + Value, }; struct Arguments { @@ -32,6 +33,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str replace") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .required("find", SyntaxShape::String, "the pattern to find") .required("replace", SyntaxShape::String, "the replacement pattern") .rest( diff --git a/crates/nu-command/src/strings/str_/reverse.rs b/crates/nu-command/src/strings/str_/reverse.rs index 6515f1285..8e8681dd1 100644 --- a/crates/nu-command/src/strings/str_/reverse.rs +++ b/crates/nu-command/src/strings/str_/reverse.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; #[derive(Clone)] pub struct SubCommand; @@ -16,6 +16,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str reverse") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .rest( "rest", SyntaxShape::CellPath, diff --git a/crates/nu-command/src/strings/str_/rpad.rs b/crates/nu-command/src/strings/str_/rpad.rs index e513dc326..b4b87c4f1 100644 --- a/crates/nu-command/src/strings/str_/rpad.rs +++ b/crates/nu-command/src/strings/str_/rpad.rs @@ -4,7 +4,7 @@ use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; struct Arguments { length: Option, @@ -28,6 +28,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str rpad") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .required_named("length", SyntaxShape::Int, "length to pad to", Some('l')) .required_named( "character", diff --git a/crates/nu-command/src/strings/str_/starts_with.rs b/crates/nu-command/src/strings/str_/starts_with.rs index 8445580a2..375401163 100644 --- a/crates/nu-command/src/strings/str_/starts_with.rs +++ b/crates/nu-command/src/strings/str_/starts_with.rs @@ -5,7 +5,7 @@ use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::Category; use nu_protocol::Spanned; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; struct Arguments { substring: String, @@ -29,6 +29,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str starts-with") + .input_output_types(vec![(Type::String, Type::Bool)]) + .vectorizes_over_list(true) .required("string", SyntaxShape::String, "the string to match") .rest( "rest", diff --git a/crates/nu-command/src/strings/str_/substring.rs b/crates/nu-command/src/strings/str_/substring.rs index fb0c6243b..6eb738c7a 100644 --- a/crates/nu-command/src/strings/str_/substring.rs +++ b/crates/nu-command/src/strings/str_/substring.rs @@ -3,7 +3,7 @@ use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value}; use std::cmp::Ordering; #[derive(Clone)] @@ -38,6 +38,8 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str substring") + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) .required( "range", SyntaxShape::Any, diff --git a/crates/nu-command/src/strings/str_/trim/trim_.rs b/crates/nu-command/src/strings/str_/trim/trim_.rs index 8fc00abcf..c38ed3cc0 100644 --- a/crates/nu-command/src/strings/str_/trim/trim_.rs +++ b/crates/nu-command/src/strings/str_/trim/trim_.rs @@ -4,7 +4,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, + Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -39,7 +39,9 @@ impl Command for SubCommand { fn signature(&self) -> Signature { Signature::build("str trim") - .rest( + .input_output_types(vec![(Type::String, Type::String)]) + .vectorizes_over_list(true) + .rest( "rest", SyntaxShape::CellPath, "optionally trim text by column paths", diff --git a/crates/nu-command/src/viewers/griddle.rs b/crates/nu-command/src/viewers/griddle.rs index d85f1bebe..83b4bb782 100644 --- a/crates/nu-command/src/viewers/griddle.rs +++ b/crates/nu-command/src/viewers/griddle.rs @@ -7,7 +7,7 @@ use nu_protocol::{ ast::{Call, PathMember}, engine::{Command, EngineState, Stack}, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, - SyntaxShape, Value, + SyntaxShape, Type, Value, }; use nu_term_grid::grid::{Alignment, Cell, Direction, Filling, Grid, GridOptions}; use nu_utils::get_ls_colors; @@ -26,6 +26,11 @@ impl Command for Griddle { fn signature(&self) -> nu_protocol::Signature { Signature::build("grid") + .input_output_types(vec![ + (Type::List(Box::new(Type::Any)), Type::String), + (Type::Record(vec![]), Type::String), + (Type::Table(vec![]), Type::String), + ]) .named( "width", SyntaxShape::Int, diff --git a/crates/nu-protocol/Cargo.toml b/crates/nu-protocol/Cargo.toml index ca2e0d16e..59a12f5ec 100644 --- a/crates/nu-protocol/Cargo.toml +++ b/crates/nu-protocol/Cargo.toml @@ -22,6 +22,8 @@ miette = { version = "5.1.0", features = ["fancy-no-backtrace"] } num-format = "0.4.3" serde = {version = "1.0.130", features = ["derive"]} serde_json = { version = "1.0", optional = true } +strum = "0.24" +strum_macros = "0.24" sys-locale = "0.2.0" thiserror = "1.0.31" typetag = "0.1.8" diff --git a/crates/nu-protocol/src/signature.rs b/crates/nu-protocol/src/signature.rs index d3af329ec..de2a0a084 100644 --- a/crates/nu-protocol/src/signature.rs +++ b/crates/nu-protocol/src/signature.rs @@ -110,9 +110,12 @@ pub struct Signature { pub required_positional: Vec, pub optional_positional: Vec, pub rest_positional: Option, + pub vectorizes_over_list: bool, pub named: Vec, pub input_type: Type, pub output_type: Type, + pub input_output_types: Vec<(Type, Type)>, + pub allow_variants_without_examples: bool, pub is_filter: bool, pub creates_scope: bool, // Signature category used to classify commands stored in the list of declarations @@ -142,8 +145,11 @@ impl Signature { required_positional: vec![], optional_positional: vec![], rest_positional: None, + vectorizes_over_list: false, input_type: Type::Any, output_type: Type::Any, + input_output_types: vec![], + allow_variants_without_examples: false, named: vec![], is_filter: false, creates_scope: false, @@ -255,6 +261,22 @@ impl Signature { self } + /// Is this command capable of operating on its input via cell paths? + pub fn operates_on_cell_paths(&self) -> bool { + self.required_positional + .iter() + .chain(self.rest_positional.iter()) + .any(|pos| { + matches!( + pos, + PositionalArg { + shape: SyntaxShape::CellPath, + .. + } + ) + }) + } + /// Add an optional named flag argument to the signature pub fn named( mut self, @@ -335,6 +357,17 @@ impl Signature { self } + pub fn vectorizes_over_list(mut self, vectorizes_over_list: bool) -> Signature { + self.vectorizes_over_list = vectorizes_over_list; + self + } + + /// Set the input-output type signature variants of the command + pub fn input_output_types(mut self, input_output_types: Vec<(Type, Type)>) -> Signature { + self.input_output_types = input_output_types; + self + } + /// Changes the signature category pub fn category(mut self, category: Category) -> Signature { self.category = category; @@ -348,6 +381,12 @@ impl Signature { self } + // Is it allowed for the type signature to feature a variant that has no corresponding example? + pub fn allow_variants_without_examples(mut self, allow: bool) -> Signature { + self.allow_variants_without_examples = allow; + self + } + pub fn call_signature(&self) -> String { let mut one_liner = String::new(); one_liner.push_str(&self.name); diff --git a/crates/nu-protocol/src/syntax_shape.rs b/crates/nu-protocol/src/syntax_shape.rs index 74aaa071e..4b9929e8b 100644 --- a/crates/nu-protocol/src/syntax_shape.rs +++ b/crates/nu-protocol/src/syntax_shape.rs @@ -97,6 +97,9 @@ pub enum SyntaxShape { /// A custom shape with custom completion logic Custom(Box, DeclId), + + /// Nothing + Nothing, } impl SyntaxShape { @@ -127,14 +130,15 @@ impl SyntaxShape { SyntaxShape::Number => Type::Number, SyntaxShape::Operator => Type::Any, SyntaxShape::Range => Type::Any, - SyntaxShape::Record => Type::Record(vec![]), // FIXME: Add actual record type + SyntaxShape::Record => Type::Record(vec![]), // FIXME: What role should fields play in the Record type? SyntaxShape::RowCondition => Type::Bool, SyntaxShape::Boolean => Type::Bool, SyntaxShape::Signature => Type::Signature, SyntaxShape::String => Type::String, - SyntaxShape::Table => Type::List(Box::new(Type::Any)), // FIXME: Tables should have better types + SyntaxShape::Table => Type::List(Box::new(Type::Any)), // FIXME: What role should columns play in the Table type? SyntaxShape::VarWithOptType => Type::Any, SyntaxShape::Variable => Type::Any, + SyntaxShape::Nothing => Type::Any, } } } @@ -174,6 +178,7 @@ impl Display for SyntaxShape { SyntaxShape::Boolean => write!(f, "bool"), SyntaxShape::Error => write!(f, "error"), SyntaxShape::Custom(x, _) => write!(f, "custom<{}>", x), + SyntaxShape::Nothing => write!(f, "nothing"), } } } diff --git a/crates/nu-protocol/src/ty.rs b/crates/nu-protocol/src/ty.rs index 382764e42..bd7cbb6f7 100644 --- a/crates/nu-protocol/src/ty.rs +++ b/crates/nu-protocol/src/ty.rs @@ -1,10 +1,11 @@ use serde::{Deserialize, Serialize}; +use strum_macros::EnumIter; use std::fmt::Display; use crate::SyntaxShape; -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)] +#[derive(Clone, Debug, Default, EnumIter, PartialEq, Eq, Serialize, Deserialize, Hash)] pub enum Type { Int, Float, @@ -18,6 +19,7 @@ pub enum Type { Filesize, List(Box), Number, + #[default] Nothing, Record(Vec<(String, Type)>), Table(Vec<(String, Type)>), @@ -30,6 +32,39 @@ pub enum Type { } impl Type { + pub fn is_subtype(&self, other: &Type) -> bool { + match (self, other) { + (t, u) if t == u => true, + (Type::Float, Type::Number) => true, + (Type::Int, Type::Number) => true, + (_, Type::Any) => true, + (Type::List(t), Type::List(u)) if t.is_subtype(u) => true, // List is covariant + + // TODO: Currently Record types specify their field types. If we are + // going to continue to do that, then it might make sense to define + // a "structural subtyping" whereby r1 is a subtype of r2 is the + // fields of r1 are a "subset" of the fields of r2 (names are a + // subset and agree on types). However, if we do that, then we need + // a way to specify the supertype of all Records. For now, we define + // any Record to be a subtype of any other Record. This allows + // Record(vec![]) to be used as an ad-hoc supertype of all Records + // in command signatures. This comment applies to Tables also, with + // "columns" in place of "fields". + (Type::Record(_), Type::Record(_)) => true, + (Type::Table(_), Type::Table(_)) => true, + _ => false, + } + } + + pub fn is_numeric(&self) -> bool { + matches!(self, Type::Int | Type::Float | Type::Number) + } + + /// Does this type represent a data structure containing values that can be addressed using 'cell paths'? + pub fn accepts_cell_paths(&self) -> bool { + matches!(self, Type::List(_) | Type::Record(_) | Type::Table(_)) + } + pub fn to_shape(&self) -> SyntaxShape { match self { Type::Int => SyntaxShape::Int, @@ -44,7 +79,7 @@ impl Type { Type::Filesize => SyntaxShape::Filesize, Type::List(x) => SyntaxShape::List(Box::new(x.to_shape())), Type::Number => SyntaxShape::Number, - Type::Nothing => SyntaxShape::Any, + Type::Nothing => SyntaxShape::Nothing, Type::Record(_) => SyntaxShape::Record, Type::Table(_) => SyntaxShape::Table, Type::ListStream => SyntaxShape::List(Box::new(SyntaxShape::Any)), @@ -100,3 +135,44 @@ impl Display for Type { } } } + +#[cfg(test)] +mod tests { + use super::Type; + use strum::IntoEnumIterator; + + mod subtype_relation { + use super::*; + + #[test] + fn test_reflexivity() { + for ty in Type::iter() { + assert!(ty.is_subtype(&ty)); + } + } + + #[test] + fn test_any_is_top_type() { + for ty in Type::iter() { + assert!(ty.is_subtype(&Type::Any)); + } + } + + #[test] + fn test_number_supertype() { + assert!(Type::Int.is_subtype(&Type::Number)); + assert!(Type::Float.is_subtype(&Type::Number)); + } + + #[test] + fn test_list_covariance() { + for ty1 in Type::iter() { + for ty2 in Type::iter() { + let list_ty1 = Type::List(Box::new(ty1.clone())); + let list_ty2 = Type::List(Box::new(ty2.clone())); + assert_eq!(list_ty1.is_subtype(&list_ty2), ty1.is_subtype(&ty2)); + } + } + } + } +} diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index 5303cad8b..c9562cae8 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -399,7 +399,11 @@ impl Value { match &ty { Some(x) => { if &val_ty != x { - ty = Some(Type::Any) + if x.is_numeric() && val_ty.is_numeric() { + ty = Some(Type::Number) + } else { + ty = Some(Type::Any) + } } } None => ty = Some(val_ty), @@ -3181,4 +3185,68 @@ mod tests { assert!(!one_column_with_empty_string_and_one_value_with_a_string.is_empty()); } } + + mod get_type { + use crate::Type; + + use super::*; + + #[test] + fn test_list() { + let list_of_ints = Value::List { + vals: vec![Value::Int { + val: 0, + span: Span::unknown(), + }], + span: Span::unknown(), + }; + let list_of_floats = Value::List { + vals: vec![Value::Float { + val: 0.0, + span: Span::unknown(), + }], + span: Span::unknown(), + }; + let list_of_ints_and_floats = Value::List { + vals: vec![ + Value::Int { + val: 0, + span: Span::unknown(), + }, + Value::Float { + val: 0.0, + span: Span::unknown(), + }, + ], + span: Span::unknown(), + }; + let list_of_ints_and_floats_and_bools = Value::List { + vals: vec![ + Value::Int { + val: 0, + span: Span::unknown(), + }, + Value::Float { + val: 0.0, + span: Span::unknown(), + }, + Value::Bool { + val: false, + span: Span::unknown(), + }, + ], + span: Span::unknown(), + }; + assert_eq!(list_of_ints.get_type(), Type::List(Box::new(Type::Int))); + assert_eq!(list_of_floats.get_type(), Type::List(Box::new(Type::Float))); + assert_eq!( + list_of_ints_and_floats_and_bools.get_type(), + Type::List(Box::new(Type::Any)) + ); + assert_eq!( + list_of_ints_and_floats.get_type(), + Type::List(Box::new(Type::Number)) + ); + } + } }