diff --git a/crates/nu-command/src/bytes/length.rs b/crates/nu-command/src/bytes/length.rs new file mode 100644 index 0000000000..4094abaffb --- /dev/null +++ b/crates/nu-command/src/bytes/length.rs @@ -0,0 +1,118 @@ +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, Value}; + +#[derive(Clone)] +pub struct BytesLen; + +impl Command for BytesLen { + fn name(&self) -> &str { + "bytes length" + } + + fn signature(&self) -> Signature { + Signature::build("bytes length") + .rest( + "rest", + SyntaxShape::CellPath, + "optionally find length of binary by column paths", + ) + .category(Category::Bytes) + } + + fn usage(&self) -> &str { + "Output the length of any bytes in the pipeline" + } + + fn search_terms(&self) -> Vec<&str> { + vec!["len", "size", "count"] + } + + 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: "0x[1F FF AA AB] | bytes length", + result: Some(Value::test_int(4)), + }, + Example { + description: "Return the lengths of multiple strings", + example: "[0x[1F FF AA AB] 0x[1F]] | bytes length", + result: Some(Value::List { + vals: vec![Value::test_int(4), Value::test_int(1)], + span: Span::test_data(), + }), + }, + ] + } +} + +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)?; + if column_paths.is_empty() { + input.map(move |v| action(&v, head), engine_state.ctrlc.clone()) + } else { + input.map( + move |mut v| { + for path in &column_paths { + let r = + v.update_cell_path(&path.members, Box::new(move |old| action(old, head))); + if let Err(error) = r { + return Value::Error { error }; + } + } + v + }, + engine_state.ctrlc.clone(), + ) + } +} + +fn action(input: &Value, head: Span) -> Value { + match input { + Value::Binary { 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 bytes.", + other.get_type() + ), + head, + ), + }, + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(BytesLen {}) + } +} diff --git a/crates/nu-command/src/bytes/mod.rs b/crates/nu-command/src/bytes/mod.rs new file mode 100644 index 0000000000..0eda47d6f3 --- /dev/null +++ b/crates/nu-command/src/bytes/mod.rs @@ -0,0 +1,3 @@ +mod length; + +pub use length::BytesLen; diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 0ecc2e5b0d..b3f3418ba4 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -207,6 +207,11 @@ pub fn create_default_context(cwd: impl AsRef) -> EngineState { StrUpcase }; + // Bytes + bind_command! { + BytesLen + } + // FileSystem bind_command! { Cd, diff --git a/crates/nu-command/src/lib.rs b/crates/nu-command/src/lib.rs index 1d58fc3242..b0c5abed05 100644 --- a/crates/nu-command/src/lib.rs +++ b/crates/nu-command/src/lib.rs @@ -1,3 +1,4 @@ +mod bytes; mod charting; mod conversions; mod core_commands; @@ -24,6 +25,7 @@ mod strings; mod system; mod viewers; +pub use bytes::*; pub use charting::*; pub use conversions::*; pub use core_commands::*; diff --git a/crates/nu-protocol/src/signature.rs b/crates/nu-protocol/src/signature.rs index 39b03091e9..0923d67ff0 100644 --- a/crates/nu-protocol/src/signature.rs +++ b/crates/nu-protocol/src/signature.rs @@ -43,6 +43,7 @@ pub enum Category { Default, Conversions, Core, + Bytes, Date, Env, Experimental, @@ -91,6 +92,7 @@ impl std::fmt::Display for Category { Category::Chart => "chart", Category::Custom(name) => name, Category::Deprecated => "deprecated", + Category::Bytes => "bytes", }; write!(f, "{}", msg)