add bytes starts-with command (#5950)
* refactor operate, make it generic * refactor operate, add starts with command * add comment * remove useless file
This commit is contained in:
parent
89acbda877
commit
390d06d4e7
|
@ -1,3 +1,4 @@
|
||||||
|
use super::{operate, BytesArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
|
@ -8,6 +9,16 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BytesLen;
|
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 {
|
impl Command for BytesLen {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"bytes length"
|
"bytes length"
|
||||||
|
@ -38,7 +49,14 @@ impl Command for BytesLen {
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> 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> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -60,48 +78,10 @@ impl Command for BytesLen {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn length(input: &[u8], _arg: &Arguments, span: Span) -> Value {
|
||||||
engine_state: &EngineState,
|
Value::Int {
|
||||||
stack: &mut Stack,
|
val: input.len() as i64,
|
||||||
call: &Call,
|
span,
|
||||||
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,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,77 @@
|
||||||
mod length;
|
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 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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
122
crates/nu-command/src/bytes/starts_with.rs
Normal file
122
crates/nu-command/src/bytes/starts_with.rs
Normal file
|
@ -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 {})
|
||||||
|
}
|
||||||
|
}
|
|
@ -209,7 +209,8 @@ pub fn create_default_context(cwd: impl AsRef<Path>) -> EngineState {
|
||||||
|
|
||||||
// Bytes
|
// Bytes
|
||||||
bind_command! {
|
bind_command! {
|
||||||
BytesLen
|
BytesLen,
|
||||||
|
BytesStartsWith
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileSystem
|
// FileSystem
|
||||||
|
|
Loading…
Reference in New Issue
Block a user