diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index d60c06a3b0..e85e93eeee 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -107,6 +107,7 @@ pub fn create_default_context() -> EngineState { StrDowncase, StrEndswith, StrIndexOf, + StrLength, StrFindReplace, StrKebabCase, StrPascalCase, diff --git a/crates/nu-command/src/strings/str_/length.rs b/crates/nu-command/src/strings/str_/length.rs new file mode 100644 index 0000000000..df082e9a7e --- /dev/null +++ b/crates/nu-command/src/strings/str_/length.rs @@ -0,0 +1,112 @@ +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}; + +#[derive(Clone)] +pub struct SubCommand; + +impl Command for SubCommand { + fn name(&self) -> &str { + "str length" + } + + fn signature(&self) -> Signature { + Signature::build("str length").rest( + "rest", + SyntaxShape::CellPath, + "optionally find length of text by column paths", + ) + } + + fn usage(&self) -> &str { + "outputs the lengths of the strings in the pipeline" + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + operate(engine_state, stack, call, input) + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Return the lengths of multiple strings", + example: "'hello' | str length", + result: Some(Value::test_int(5)), + }, + Example { + description: "Return the lengths of multiple strings", + example: "['hi' 'there'] | str length", + result: Some(Value::List { + vals: vec![Value::test_int(2), Value::test_int(5)], + span: Span::unknown(), + }), + }, + ] + } +} + +fn operate( + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, +) -> Result { + let head = call.head; + let column_paths: Vec = call.rest(engine_state, stack, 0)?; + input.map( + move |v| { + if column_paths.is_empty() { + action(&v, head) + } else { + let mut ret = v; + for path in &column_paths { + let r = + ret.update_cell_path(&path.members, Box::new(move |old| action(old, head))); + if let Err(error) = r { + return Value::Error { error }; + } + } + ret + } + }, + engine_state.ctrlc.clone(), + ) +} + +fn action(input: &Value, head: Span) -> Value { + match input { + Value::String { val, .. } => Value::Int { + val: val.len() as i64, + span: head, + }, + other => Value::Error { + error: ShellError::UnsupportedInput( + format!( + "Input's type is {}. This command only works with strings.", + other.get_type() + ), + Span::unknown(), + ), + }, + } +} + +#[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/str_/mod.rs b/crates/nu-command/src/strings/str_/mod.rs index ca656a18d4..18a5ada94e 100644 --- a/crates/nu-command/src/strings/str_/mod.rs +++ b/crates/nu-command/src/strings/str_/mod.rs @@ -6,6 +6,7 @@ mod downcase; mod ends_with; mod find_replace; mod index_of; +mod length; pub use capitalize::SubCommand as StrCapitalize; pub use case::*; @@ -15,3 +16,4 @@ pub use downcase::SubCommand as StrDowncase; pub use ends_with::SubCommand as StrEndswith; pub use find_replace::SubCommand as StrFindReplace; pub use index_of::SubCommand as StrIndexOf; +pub use length::SubCommand as StrLength;