diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index f89be8deb4..3eb2d5659a 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -174,6 +174,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { SplitColumn, SplitRow, SplitWords, + StrEscapeGlob, Str, StrCapitalize, StrContains, diff --git a/crates/nu-command/src/strings/str_/escape_glob.rs b/crates/nu-command/src/strings/str_/escape_glob.rs new file mode 100644 index 0000000000..b2740bcd20 --- /dev/null +++ b/crates/nu-command/src/strings/str_/escape_glob.rs @@ -0,0 +1,92 @@ +use nu_cmd_base::input_handler::{operate, CmdArgument}; +use nu_engine::CallExt; +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, Type, Value}; + +#[derive(Clone)] +pub struct SubCommand; + +struct Arguments { + cell_paths: Option>, +} + +impl CmdArgument for Arguments { + fn take_cell_paths(&mut self) -> Option> { + self.cell_paths.take() + } +} + +impl Command for SubCommand { + fn name(&self) -> &str { + "str escape-glob" + } + + fn signature(&self) -> Signature { + Signature::build("str escape-glob") + .input_output_types(vec![ + (Type::String, Type::String), + ( + Type::List(Box::new(Type::String)), + Type::List(Box::new(Type::String)), + ), + (Type::Table(vec![]), Type::Table(vec![])), + (Type::Record(vec![]), Type::Record(vec![])), + ]) + .allow_variants_without_examples(true) + .rest( + "rest", + SyntaxShape::CellPath, + "For a data structure input, turn strings at the given cell paths into substrings.", + ) + .category(Category::Strings) + } + + fn usage(&self) -> &str { + "Escape glob pattern." + } + + fn search_terms(&self) -> Vec<&str> { + vec!["pattern", "list", "ls"] + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let cell_paths: Vec = call.rest(engine_state, stack, 0)?; + let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths); + let args = Arguments { cell_paths }; + operate(action, args, input, call.head, engine_state.ctrlc.clone()) + } + + fn examples(&self) -> Vec { + vec![Example { + description: "escape glob pattern before list", + example: r#"let f = 'test[a]'; ls ($f | str escape-glob)"#, + result: None, + }] + } +} + +fn action(input: &Value, _args: &Arguments, head: Span) -> Value { + match input { + Value::String { val: s, .. } => Value::string(nu_glob::Pattern::escape(s), input.span()), + // Propagate errors by explicitly matching them before the final case. + Value::Error { .. } => input.clone(), + other => Value::error( + ShellError::OnlySupportsThisInputType { + exp_input_type: "string".into(), + wrong_type: other.get_type().to_string(), + dst_span: head, + src_span: other.span(), + }, + head, + ), + } +} diff --git a/crates/nu-command/src/strings/str_/mod.rs b/crates/nu-command/src/strings/str_/mod.rs index fb34ace833..c3f989361c 100644 --- a/crates/nu-command/src/strings/str_/mod.rs +++ b/crates/nu-command/src/strings/str_/mod.rs @@ -2,6 +2,7 @@ mod case; mod contains; mod distance; mod ends_with; +mod escape_glob; mod expand; mod index_of; mod join; @@ -17,6 +18,7 @@ pub use case::*; pub use contains::SubCommand as StrContains; pub use distance::SubCommand as StrDistance; pub use ends_with::SubCommand as StrEndswith; +pub use escape_glob::SubCommand as StrEscapeGlob; pub use expand::SubCommand as StrExpand; pub use index_of::SubCommand as StrIndexOf; pub use join::*;