Declare input and output types of commands (#6796)

* Add failing test that list of ints and floats is List<Number>

* 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>
This commit is contained in:
Dan Davison
2022-11-09 16:55:05 -05:00
committed by GitHub
parent f878276de7
commit df94052180
238 changed files with 2315 additions and 756 deletions

View File

@ -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())),
},
]
}

View File

@ -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())),
},
]
}

View File

@ -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",

View File

@ -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<Example> {
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![

View File

@ -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(

View File

@ -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(

View File

@ -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(

View File

@ -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
},
]
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -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",

View File

@ -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(

View File

@ -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(

View File

@ -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)

View File

@ -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)
}

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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)
}

View File

@ -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,

View File

@ -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",

View File

@ -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())),
}]
}
}

View File

@ -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",

View File

@ -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,

View File

@ -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",

View File

@ -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,

View File

@ -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)
}

View File

@ -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<String> {
names
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::Version;
use crate::test_examples;
test_examples(Version {})
}
}