Skip to content

Commit

Permalink
Add --env and --wrapped flags to def (nushell#10566)
Browse files Browse the repository at this point in the history
  • Loading branch information
kubouch authored Oct 2, 2023
1 parent 0d367af commit eb6870c
Show file tree
Hide file tree
Showing 20 changed files with 389 additions and 195 deletions.
16 changes: 14 additions & 2 deletions crates/nu-cmd-lang/src/core_commands/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ 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("def_name", SyntaxShape::String, "command name")
.required("params", SyntaxShape::Signature, "parameters")
.required("body", SyntaxShape::Closure(None), "body of the definition")
.required("block", SyntaxShape::Closure(None), "body of the definition")
.switch("env", "keep the environment defined inside the command", None)
.switch("wrapped", "treat unknown flags and arguments as strings (requires ...rest-like parameter in signature)", None)
.category(Category::Core)
}

Expand Down Expand Up @@ -56,6 +58,16 @@ impl Command for Def {
example: r#"def say-sth [sth: string] { echo $sth }; say-sth hi"#,
result: Some(Value::test_string("hi")),
},
Example {
description: "Set environment variable by call a custom command",
example: r#"def --env foo [] { $env.BAR = "BAZ" }; foo; $env.BAR"#,
result: Some(Value::test_string("BAZ")),
},
Example {
description: "Define a custom wrapper for an external command",
example: r#"def --wrapped my-echo [...rest] { echo $rest }; my-echo spam"#,
result: Some(Value::test_list(vec![Value::test_string("spam")])),
},
]
}
}
4 changes: 3 additions & 1 deletion crates/nu-cmd-lang/src/core_commands/export_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ 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("def_name", SyntaxShape::String, "command name")
.required("params", SyntaxShape::Signature, "parameters")
.required("block", SyntaxShape::Block, "body of the definition")
.switch("env", "keep the environment defined inside the command", None)
.switch("wrapped", "treat unknown flags and arguments as strings (requires ...rest-like parameter in signature)", None)
.category(Category::Core)
}

Expand Down
2 changes: 1 addition & 1 deletion crates/nu-cmd-lang/src/core_commands/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl Command for Module {
},
Example {
description: "Define a custom command that participates in the environment in a module and call it",
example: r#"module foo { export def-env bar [] { $env.FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
example: r#"module foo { export def --env bar [] { $env.FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
result: Some(Value::test_string("BAZ")),
},
]
Expand Down
2 changes: 1 addition & 1 deletion crates/nu-cmd-lang/src/core_commands/use_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ This command is a parser keyword. For details, check:
},
Example {
description: "Define a custom command that participates in the environment in a module and call it",
example: r#"module foo { export def-env bar [] { $env.FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
example: r#"module foo { export def --env bar [] { $env.FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
result: Some(Value::test_string("BAZ")),
},
Example {
Expand Down
2 changes: 1 addition & 1 deletion crates/nu-command/src/debug/view_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ impl Command for ViewSource {
},
Example {
description: "View the source of a custom command, which participates in the caller environment",
example: r#"def-env foo [] { $env.BAR = 'BAZ' }; view source foo"#,
example: r#"def --env foo [] { $env.BAR = 'BAZ' }; view source foo"#,
result: Some(Value::test_string("def foo [] { $env.BAR = 'BAZ' }")),
},
Example {
Expand Down
126 changes: 116 additions & 10 deletions crates/nu-command/tests/commands/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fn def_errors_with_no_space_between_params_and_name_1() {

#[test]
fn def_errors_with_no_space_between_params_and_name_2() {
let actual = nu!("def-env test-command() {}");
let actual = nu!("def --env test-command() {}");

assert!(actual.err.contains("expected space"));
}
Expand Down Expand Up @@ -156,15 +156,6 @@ fn def_with_paren_params() {
assert_eq!(actual.out, "3");
}

#[test]
fn extern_with_block() {
let actual = nu!(
"extern-wrapped foo [...rest] { print ($rest | str join ',' ) }; foo --bar baz -- -q -u -x"
);

assert_eq!(actual.out, "--bar,baz,--,-q,-u,-x");
}

#[test]
fn def_default_value_shouldnt_restrict_explicit_type() {
let actual = nu!("def foo [x: any = null] { $x }; foo 1");
Expand Down Expand Up @@ -193,3 +184,118 @@ fn def_boolean_flags() {
let actual = nu!("def foo [--x: bool] { $x == null }; foo");
assert_eq!(actual.out, "true");
}

#[test]
fn def_wrapped_with_block() {
let actual = nu!(
"def --wrapped foo [...rest] { print ($rest | str join ',' ) }; foo --bar baz -- -q -u -x"
);

assert_eq!(actual.out, "--bar,baz,--,-q,-u,-x");
}

#[test]
fn def_wrapped_from_module() {
let actual = nu!(r#"module spam {
export def --wrapped my-echo [...rest] { ^echo $rest }
}
use spam
spam my-echo foo -b -as -9 --abc -- -Dxmy=AKOO - bar
"#);

assert!(actual
.out
.contains("foo -b -as -9 --abc -- -Dxmy=AKOO - bar"));
}

#[test]
fn def_cursed_env_flag_positions() {
let actual = nu!("def spam --env [] { $env.SPAM = 'spam' }; spam; $env.SPAM");
assert_eq!(actual.out, "spam");

let actual =
nu!("def spam --env []: nothing -> nothing { $env.SPAM = 'spam' }; spam; $env.SPAM");
assert_eq!(actual.out, "spam");
}

#[test]
#[ignore = "TODO: Investigate why it's not working, it might be the signature parsing"]
fn def_cursed_env_flag_positions_2() {
let actual = nu!("def spam [] --env { $env.SPAM = 'spam' }; spam; $env.SPAM");
assert_eq!(actual.out, "spam");

let actual = nu!("def spam [] { $env.SPAM = 'spam' } --env; spam; $env.SPAM");
assert_eq!(actual.out, "spam");

let actual =
nu!("def spam []: nothing -> nothing { $env.SPAM = 'spam' } --env; spam; $env.SPAM");
assert_eq!(actual.out, "spam");
}

#[test]
fn export_def_cursed_env_flag_positions() {
let actual = nu!("export def spam --env [] { $env.SPAM = 'spam' }; spam; $env.SPAM");
assert_eq!(actual.out, "spam");

let actual =
nu!("export def spam --env []: nothing -> nothing { $env.SPAM = 'spam' }; spam; $env.SPAM");
assert_eq!(actual.out, "spam");
}

#[test]
#[ignore = "TODO: Investigate why it's not working, it might be the signature parsing"]
fn export_def_cursed_env_flag_positions_2() {
let actual = nu!("export def spam [] --env { $env.SPAM = 'spam' }; spam; $env.SPAM");
assert_eq!(actual.out, "spam");

let actual = nu!("export def spam [] { $env.SPAM = 'spam' } --env; spam; $env.SPAM");
assert_eq!(actual.out, "spam");

let actual =
nu!("export def spam []: nothing -> nothing { $env.SPAM = 'spam' } --env; spam; $env.SPAM");
assert_eq!(actual.out, "spam");
}

#[test]
fn def_cursed_wrapped_flag_positions() {
let actual = nu!("def spam --wrapped [...rest] { $rest.0 }; spam --foo");
assert_eq!(actual.out, "--foo");

let actual = nu!("def spam --wrapped [...rest]: nothing -> nothing { $rest.0 }; spam --foo");
assert_eq!(actual.out, "--foo");
}

#[test]
#[ignore = "TODO: Investigate why it's not working, it might be the signature parsing"]
fn def_cursed_wrapped_flag_positions_2() {
let actual = nu!("def spam [...rest] --wrapped { $rest.0 }; spam --foo");
assert_eq!(actual.out, "--foo");

let actual = nu!("def spam [...rest] { $rest.0 } --wrapped; spam --foo");
assert_eq!(actual.out, "--foo");

let actual = nu!("def spam [...rest]: nothing -> nothing { $rest.0 } --wrapped; spam --foo");
assert_eq!(actual.out, "--foo");
}

#[test]
fn def_wrapped_missing_rest_error() {
let actual = nu!("def --wrapped spam [] {}");
assert!(actual.err.contains("missing_positional"))
}

#[test]
fn def_wrapped_wrong_rest_type_error() {
let actual = nu!("def --wrapped spam [...eggs: list<string>] { $eggs }");
assert!(actual.err.contains("type_mismatch_help"));
assert!(actual.err.contains("of ...eggs to 'string'"));
}

#[test]
fn def_env_wrapped() {
let actual = nu!(
"def --env --wrapped spam [...eggs: string] { $env.SPAM = $eggs.0 }; spam bacon; $env.SPAM"
);
assert_eq!(actual.out, "bacon");
}
2 changes: 1 addition & 1 deletion crates/nu-command/tests/commands/use_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ fn use_main_4() {
#[test]
fn use_main_def_env() {
let inp = &[
r#"module spam { export def-env main [] { $env.SPAM = "spam" } }"#,
r#"module spam { export def --env main [] { $env.SPAM = "spam" } }"#,
r#"use spam"#,
r#"spam"#,
r#"$env.SPAM"#,
Expand Down
Loading

0 comments on commit eb6870c

Please sign in to comment.