diff options
author | WindSoilder <WindSoilder@outlook.com> | 2022-07-05 19:42:01 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-05 06:42:01 -0500 |
commit | 390d06d4e7383d9fee814713bda8d1aaca858c5a (patch) | |
tree | 396996bda56715f59944a7d21adf95423436ef39 | |
parent | 89acbda8772522bafb0c7e5ca072c946e73bb3d3 (diff) |
add bytes starts-with command (#5950)0.65.0
* refactor operate, make it generic
* refactor operate, add starts with command
* add comment
* remove useless file
-rw-r--r-- | crates/nu-command/src/bytes/length.rs | 66 | ||||
-rw-r--r-- | crates/nu-command/src/bytes/mod.rs | 74 | ||||
-rw-r--r-- | crates/nu-command/src/bytes/starts_with.rs | 122 | ||||
-rw-r--r-- | crates/nu-command/src/default_context.rs | 3 |
4 files changed, 221 insertions, 44 deletions
diff --git a/crates/nu-command/src/bytes/length.rs b/crates/nu-command/src/bytes/length.rs index 4094abaff..9e8dbb162 100644 --- a/crates/nu-command/src/bytes/length.rs +++ b/crates/nu-command/src/bytes/length.rs @@ -1,3 +1,4 @@ +use super::{operate, BytesArgument}; use nu_engine::CallExt; use nu_protocol::ast::Call; use nu_protocol::ast::CellPath; @@ -8,6 +9,16 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap #[derive(Clone)] pub struct BytesLen; +struct Arguments { + column_paths: Option<Vec<CellPath>>, +} + +impl BytesArgument for Arguments { + fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { + self.column_paths.take() + } +} + impl Command for BytesLen { fn name(&self) -> &str { "bytes length" @@ -38,7 +49,14 @@ impl Command for BytesLen { call: &Call, input: PipelineData, ) -> Result<PipelineData, ShellError> { - operate(engine_state, stack, call, input) + let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; + let column_paths = if column_paths.is_empty() { + None + } else { + Some(column_paths) + }; + let arg = Arguments { column_paths }; + operate(length, arg, input, call.head, engine_state.ctrlc.clone()) } fn examples(&self) -> Vec<Example> { @@ -60,48 +78,10 @@ impl Command for BytesLen { } } -fn operate( - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - input: PipelineData, -) -> Result<PipelineData, ShellError> { - let head = call.head; - let column_paths: Vec<CellPath> = 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, - ), - }, +fn length(input: &[u8], _arg: &Arguments, span: Span) -> Value { + Value::Int { + val: input.len() as i64, + span, } } diff --git a/crates/nu-command/src/bytes/mod.rs b/crates/nu-command/src/bytes/mod.rs index 0eda47d6f..30cdd039e 100644 --- a/crates/nu-command/src/bytes/mod.rs +++ b/crates/nu-command/src/bytes/mod.rs @@ -1,3 +1,77 @@ mod length; +mod starts_with; +use nu_protocol::ast::CellPath; +use nu_protocol::{PipelineData, ShellError, Span, Value}; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; pub use length::BytesLen; +pub use starts_with::BytesStartsWith; + +trait BytesArgument { + fn take_column_paths(&mut self) -> Option<Vec<CellPath>>; +} + +/// map input pipeline data, for each elements, if it's Binary, invoke relative `cmd` with `arg`. +fn operate<C, A>( + cmd: C, + mut arg: A, + input: PipelineData, + span: Span, + ctrlc: Option<Arc<AtomicBool>>, +) -> Result<PipelineData, ShellError> +where + A: BytesArgument + Send + Sync + 'static, + C: Fn(&[u8], &A, Span) -> Value + Send + Sync + 'static + Clone + Copy, +{ + match arg.take_column_paths() { + None => input.map( + move |v| match v { + Value::Binary { + val, + span: val_span, + } => cmd(&val, &arg, val_span), + other => Value::Error { + error: ShellError::UnsupportedInput( + format!( + "Input's type is {}. This command only works with bytes.", + other.get_type() + ), + span, + ), + }, + }, + ctrlc, + ), + Some(column_paths) => { + let arg = Arc::new(arg); + input.map( + move |mut v| { + for path in &column_paths { + let opt = arg.clone(); + let r = v.update_cell_path( + &path.members, + Box::new(move |old| { + match old { + Value::Binary {val, span: val_span} => cmd(val, &opt, *val_span), + other => Value::Error { + error: ShellError::UnsupportedInput( + format!( + "Input's type is {}. This command only works with bytes.", + other.get_type() + ), + span, + ), + }}}), + ); + if let Err(error) = r { + return Value::Error { error }; + } + } + v + }, + ctrlc, + ) + } + } +} diff --git a/crates/nu-command/src/bytes/starts_with.rs b/crates/nu-command/src/bytes/starts_with.rs new file mode 100644 index 000000000..a022530a2 --- /dev/null +++ b/crates/nu-command/src/bytes/starts_with.rs @@ -0,0 +1,122 @@ +use super::{operate, BytesArgument}; +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}; + +struct Arguments { + pattern: Vec<u8>, + column_paths: Option<Vec<CellPath>>, +} + +impl BytesArgument for Arguments { + fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { + self.column_paths.take() + } +} + +#[derive(Clone)] + +pub struct BytesStartsWith; + +impl Command for BytesStartsWith { + fn name(&self) -> &str { + "bytes starts-with" + } + + fn signature(&self) -> Signature { + Signature::build("bytes starts-with") + .required("pattern", SyntaxShape::Binary, "the pattern to match") + .rest( + "rest", + SyntaxShape::CellPath, + "optionally matches prefix of text by column paths", + ) + .category(Category::Bytes) + } + + fn usage(&self) -> &str { + "Check if bytes starts with a pattern" + } + + fn search_terms(&self) -> Vec<&str> { + vec!["pattern", "match", "find", "search"] + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result<PipelineData, ShellError> { + let pattern: Vec<u8> = call.req(engine_state, stack, 0)?; + let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; + let column_paths = if column_paths.is_empty() { + None + } else { + Some(column_paths) + }; + let arg = Arguments { + pattern, + column_paths, + }; + operate( + starts_with, + arg, + input, + call.head, + engine_state.ctrlc.clone(), + ) + } + + fn examples(&self) -> Vec<Example> { + vec![ + Example { + description: "Checks if binary starts with `0x[1F FF AA]`", + example: "0x[1F FF AA AA] | bytes starts-with 0x[1F FF AA]", + result: Some(Value::Bool { + val: true, + span: Span::test_data(), + }), + }, + Example { + description: "Checks if binary starts with `0x[1F]`", + example: "0x[1F FF AA AA] | bytes starts-with 0x[1F]", + result: Some(Value::Bool { + val: true, + span: Span::test_data(), + }), + }, + Example { + description: "Checks if binary starts with `0x[1F]`", + example: "0x[1F FF AA AA] | bytes starts-with 0x[11]", + result: Some(Value::Bool { + val: false, + span: Span::test_data(), + }), + }, + ] + } +} + +fn starts_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value { + Value::Bool { + val: input.starts_with(pattern), + span, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(BytesStartsWith {}) + } +} diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index b3f3418ba..604ba726c 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -209,7 +209,8 @@ pub fn create_default_context(cwd: impl AsRef<Path>) -> EngineState { // Bytes bind_command! { - BytesLen + BytesLen, + BytesStartsWith } // FileSystem |