mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 07:00:37 +02:00
move 'bytes' back to commands (#10051)
# Description Moves the `bytes XXXX` commands back to the default set.
This commit is contained in:
@ -1,192 +0,0 @@
|
||||
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};
|
||||
|
||||
struct Arguments {
|
||||
added_data: Vec<u8>,
|
||||
index: Option<usize>,
|
||||
end: bool,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
pub struct BytesAdd;
|
||||
|
||||
impl Command for BytesAdd {
|
||||
fn name(&self) -> &str {
|
||||
"bytes add"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("bytes add")
|
||||
.input_output_types(vec![
|
||||
(Type::Binary, Type::Binary),
|
||||
(
|
||||
Type::List(Box::new(Type::Binary)),
|
||||
Type::List(Box::new(Type::Binary)),
|
||||
),
|
||||
(Type::Table(vec![]), Type::Table(vec![])),
|
||||
(Type::Record(vec![]), Type::Record(vec![])),
|
||||
])
|
||||
.allow_variants_without_examples(true)
|
||||
.required("data", SyntaxShape::Binary, "the binary to add")
|
||||
.named(
|
||||
"index",
|
||||
SyntaxShape::Int,
|
||||
"index to insert binary data",
|
||||
Some('i'),
|
||||
)
|
||||
.switch("end", "add to the end of binary", Some('e'))
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"for a data structure input, add bytes to the data at the given cell paths",
|
||||
)
|
||||
.category(Category::Bytes)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Add specified bytes to the input."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["append", "truncate", "padding"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let added_data: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let index: Option<usize> = call.get_flag(engine_state, stack, "index")?;
|
||||
let end = call.has_flag("end");
|
||||
|
||||
let arg = Arguments {
|
||||
added_data,
|
||||
index,
|
||||
end,
|
||||
cell_paths,
|
||||
};
|
||||
operate(add, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Add bytes `0x[AA]` to `0x[1F FF AA AA]`",
|
||||
example: "0x[1F FF AA AA] | bytes add 0x[AA]",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0xAA, 0x1F, 0xFF, 0xAA, 0xAA],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Add bytes `0x[AA BB]` to `0x[1F FF AA AA]` at index 1",
|
||||
example: "0x[1F FF AA AA] | bytes add 0x[AA BB] -i 1",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0x1F, 0xAA, 0xBB, 0xFF, 0xAA, 0xAA],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Add bytes `0x[11]` to `0x[FF AA AA]` at the end",
|
||||
example: "0x[FF AA AA] | bytes add 0x[11] -e",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0xFF, 0xAA, 0xAA, 0x11],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Add bytes `0x[11 22 33]` to `0x[FF AA AA]` at the end, at index 1(the index is start from end)",
|
||||
example: "0x[FF AA BB] | bytes add 0x[11 22 33] -e -i 1",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0xFF, 0xAA, 0x11, 0x22, 0x33, 0xBB],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn add(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => add_impl(val, args, *val_span),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => val.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "binary".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.expect_span(),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn add_impl(input: &[u8], args: &Arguments, span: Span) -> Value {
|
||||
match args.index {
|
||||
None => {
|
||||
if args.end {
|
||||
let mut added_data = args.added_data.clone();
|
||||
let mut result = input.to_vec();
|
||||
result.append(&mut added_data);
|
||||
Value::Binary { val: result, span }
|
||||
} else {
|
||||
let mut result = args.added_data.clone();
|
||||
let mut input = input.to_vec();
|
||||
result.append(&mut input);
|
||||
Value::Binary { val: result, span }
|
||||
}
|
||||
}
|
||||
Some(mut indx) => {
|
||||
let inserted_index = if args.end {
|
||||
input.len().saturating_sub(indx)
|
||||
} else {
|
||||
if indx > input.len() {
|
||||
indx = input.len()
|
||||
}
|
||||
indx
|
||||
};
|
||||
let mut result = vec![];
|
||||
let mut prev_data = input[..inserted_index].to_vec();
|
||||
result.append(&mut prev_data);
|
||||
let mut added_data = args.added_data.clone();
|
||||
result.append(&mut added_data);
|
||||
let mut after_data = input[inserted_index..].to_vec();
|
||||
result.append(&mut after_data);
|
||||
Value::Binary { val: result, span }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(BytesAdd {})
|
||||
}
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_cmd_base::util;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, Range, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BytesAt;
|
||||
|
||||
struct Arguments {
|
||||
indexes: Subbytes,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(isize, isize)> for Subbytes {
|
||||
fn from(input: (isize, isize)) -> Self {
|
||||
Self(input.0, input.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Subbytes(isize, isize);
|
||||
|
||||
impl Command for BytesAt {
|
||||
fn name(&self) -> &str {
|
||||
"bytes at"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("bytes at")
|
||||
.input_output_types(vec![
|
||||
(Type::Binary, Type::Binary),
|
||||
(
|
||||
Type::List(Box::new(Type::Binary)),
|
||||
Type::List(Box::new(Type::Binary)),
|
||||
),
|
||||
(Type::Table(vec![]), Type::Table(vec![])),
|
||||
(Type::Record(vec![]), Type::Record(vec![])),
|
||||
])
|
||||
.required("range", SyntaxShape::Range, "the range to get bytes")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"for a data structure input, get bytes from data at the given cell paths",
|
||||
)
|
||||
.category(Category::Bytes)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Get bytes defined by a range"
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["slice"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let range: Range = call.req(engine_state, stack, 0)?;
|
||||
let indexes = match util::process_range(&range) {
|
||||
Ok(idxs) => idxs.into(),
|
||||
Err(processing_error) => {
|
||||
return Err(processing_error("could not perform subbytes", call.head));
|
||||
}
|
||||
};
|
||||
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let args = Arguments {
|
||||
indexes,
|
||||
cell_paths,
|
||||
};
|
||||
|
||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Get a subbytes `0x[10 01]` from the bytes `0x[33 44 55 10 01 13]`",
|
||||
example: " 0x[33 44 55 10 01 13] | bytes at 3..<4",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0x10],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Get a subbytes `0x[10 01 13]` from the bytes `0x[33 44 55 10 01 13]`",
|
||||
example: " 0x[33 44 55 10 01 13] | bytes at 3..6",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0x10, 0x01, 0x13],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Get the remaining characters from a starting index",
|
||||
example: " { data: 0x[33 44 55 10 01 13] } | bytes at 3.. data",
|
||||
result: Some(Value::test_record(
|
||||
vec!["data"],
|
||||
vec![Value::test_binary(vec![0x10, 0x01, 0x13])],
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Get the characters from the beginning until ending index",
|
||||
example: " 0x[33 44 55 10 01 13] | bytes at ..<4",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0x33, 0x44, 0x55, 0x10],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description:
|
||||
"Or the characters from the beginning until ending index inside a table",
|
||||
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes at 1.. ColB ColC"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||
vals: vec![
|
||||
Value::Binary {
|
||||
val: vec![0x11, 0x12, 0x13],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Binary {
|
||||
val: vec![0x15, 0x16],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Binary {
|
||||
val: vec![0x18, 0x19],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
let range = &args.indexes;
|
||||
match input {
|
||||
Value::Binary { val, .. } => {
|
||||
use std::cmp::{self, Ordering};
|
||||
let len = val.len() as isize;
|
||||
|
||||
let start = if range.0 < 0 { range.0 + len } else { range.0 };
|
||||
|
||||
let end = if range.1 < 0 {
|
||||
cmp::max(range.1 + len, 0)
|
||||
} else {
|
||||
range.1
|
||||
};
|
||||
|
||||
if start < len && end >= 0 {
|
||||
match start.cmp(&end) {
|
||||
Ordering::Equal => Value::Binary {
|
||||
val: vec![],
|
||||
span: head,
|
||||
},
|
||||
Ordering::Greater => Value::Error {
|
||||
error: Box::new(ShellError::TypeMismatch {
|
||||
err_message: "End must be greater than or equal to Start".to_string(),
|
||||
span: head,
|
||||
}),
|
||||
},
|
||||
Ordering::Less => Value::Binary {
|
||||
val: {
|
||||
if end == isize::max_value() {
|
||||
val.iter().skip(start as usize).copied().collect()
|
||||
} else {
|
||||
val.iter()
|
||||
.skip(start as usize)
|
||||
.take((end - start) as usize)
|
||||
.copied()
|
||||
.collect()
|
||||
}
|
||||
},
|
||||
span: head,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
Value::Binary {
|
||||
val: vec![],
|
||||
span: head,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value::Error { .. } => input.clone(),
|
||||
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::UnsupportedInput(
|
||||
"Only binary values are supported".into(),
|
||||
format!("input type: {:?}", other.get_type()),
|
||||
head,
|
||||
// This line requires the Value::Error match above.
|
||||
other.expect_span(),
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
use nu_engine::eval_expression;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BytesBuild;
|
||||
|
||||
impl Command for BytesBuild {
|
||||
fn name(&self) -> &str {
|
||||
"bytes build"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Create bytes from the arguments."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["concatenate", "join"]
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("bytes build")
|
||||
.input_output_types(vec![(Type::Nothing, Type::Binary)])
|
||||
.rest("rest", SyntaxShape::Any, "list of bytes")
|
||||
.category(Category::Bytes)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
example: "bytes build 0x[01 02] 0x[03] 0x[04]",
|
||||
description: "Builds binary data from 0x[01 02], 0x[03], 0x[04]",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0x01, 0x02, 0x03, 0x04],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
}]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let mut output = vec![];
|
||||
for expr in call.positional_iter() {
|
||||
let val = eval_expression(engine_state, stack, expr)?;
|
||||
match val {
|
||||
Value::Binary { mut val, .. } => output.append(&mut val),
|
||||
// Explicitly propagate errors instead of dropping them.
|
||||
Value::Error { error } => return Err(*error),
|
||||
other => {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: "only binary data arguments are supported".to_string(),
|
||||
span: other.expect_span(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Binary {
|
||||
val: output,
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(BytesBuild {})
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
use nu_engine::get_full_help;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Bytes;
|
||||
|
||||
impl Command for Bytes {
|
||||
fn name(&self) -> &str {
|
||||
"bytes"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("bytes")
|
||||
.category(Category::Bytes)
|
||||
.input_output_types(vec![(Type::Nothing, Type::String)])
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Various commands for working with byte data."
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
"You must use one of the following subcommands. Using this command as-is will only produce this help message."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
Ok(Value::String {
|
||||
val: get_full_help(
|
||||
&Bytes.signature(),
|
||||
&Bytes.examples(),
|
||||
engine_state,
|
||||
stack,
|
||||
self.is_parser_keyword(),
|
||||
),
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct BytesCollect;
|
||||
|
||||
impl Command for BytesCollect {
|
||||
fn name(&self) -> &str {
|
||||
"bytes collect"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("bytes collect")
|
||||
.input_output_types(vec![(Type::List(Box::new(Type::Binary)), Type::Binary)])
|
||||
.optional(
|
||||
"separator",
|
||||
SyntaxShape::Binary,
|
||||
"optional separator to use when creating binary",
|
||||
)
|
||||
.category(Category::Bytes)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Concatenate multiple binary into a single binary, with an optional separator between each."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["join", "concatenate"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let separator: Option<Vec<u8>> = call.opt(engine_state, stack, 0)?;
|
||||
// input should be a list of binary data.
|
||||
let mut output_binary = vec![];
|
||||
for value in input {
|
||||
match value {
|
||||
Value::Binary { mut val, .. } => {
|
||||
output_binary.append(&mut val);
|
||||
// manually concat
|
||||
// TODO: make use of std::slice::Join when it's available in stable.
|
||||
if let Some(sep) = &separator {
|
||||
let mut work_sep = sep.clone();
|
||||
output_binary.append(&mut work_sep)
|
||||
}
|
||||
}
|
||||
// Explicitly propagate errors instead of dropping them.
|
||||
Value::Error { error } => return Err(*error),
|
||||
other => {
|
||||
return Err(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "binary".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: call.head,
|
||||
src_span: other.expect_span(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match separator {
|
||||
None => Ok(Value::Binary {
|
||||
val: output_binary,
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data()),
|
||||
Some(sep) => {
|
||||
if output_binary.is_empty() {
|
||||
Ok(Value::Binary {
|
||||
val: output_binary,
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
} else {
|
||||
// have push one extra separator in previous step, pop them out.
|
||||
for _ in sep {
|
||||
let _ = output_binary.pop();
|
||||
}
|
||||
Ok(Value::Binary {
|
||||
val: output_binary,
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Create a byte array from input",
|
||||
example: "[0x[11] 0x[13 15]] | bytes collect",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0x11, 0x13, 0x15],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Create a byte array from input with a separator",
|
||||
example: "[0x[11] 0x[33] 0x[44]] | bytes collect 0x[01]",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0x11, 0x01, 0x33, 0x01, 0x44],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(BytesCollect {})
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
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};
|
||||
|
||||
struct Arguments {
|
||||
pattern: Vec<u8>,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
pub struct BytesEndsWith;
|
||||
|
||||
impl Command for BytesEndsWith {
|
||||
fn name(&self) -> &str {
|
||||
"bytes ends-with"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("bytes ends-with")
|
||||
.input_output_types(vec![(Type::Binary, Type::Bool),
|
||||
(Type::Table(vec![]), Type::Table(vec![])),
|
||||
(Type::Record(vec![]), Type::Record(vec![])),
|
||||
])
|
||||
.allow_variants_without_examples(true)
|
||||
.required("pattern", SyntaxShape::Binary, "the pattern to match")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"for a data structure input, check if bytes at the given cell paths end with the pattern",
|
||||
)
|
||||
.category(Category::Bytes)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Check if bytes ends 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 cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let arg = Arguments {
|
||||
pattern,
|
||||
cell_paths,
|
||||
};
|
||||
operate(ends_with, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Checks if binary ends with `0x[AA]`",
|
||||
example: "0x[1F FF AA AA] | bytes ends-with 0x[AA]",
|
||||
result: Some(Value::test_bool(true)),
|
||||
},
|
||||
Example {
|
||||
description: "Checks if binary ends with `0x[FF AA AA]`",
|
||||
example: "0x[1F FF AA AA] | bytes ends-with 0x[FF AA AA]",
|
||||
result: Some(Value::test_bool(true)),
|
||||
},
|
||||
Example {
|
||||
description: "Checks if binary ends with `0x[11]`",
|
||||
example: "0x[1F FF AA AA] | bytes ends-with 0x[11]",
|
||||
result: Some(Value::test_bool(false)),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn ends_with(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => Value::bool(val.ends_with(&args.pattern), *val_span),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => val.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "binary".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.expect_span(),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(BytesEndsWith {})
|
||||
}
|
||||
}
|
@ -1,234 +0,0 @@
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::{Call, CellPath};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
struct Arguments {
|
||||
pattern: Vec<u8>,
|
||||
end: bool,
|
||||
all: bool,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BytesIndexOf;
|
||||
|
||||
impl Command for BytesIndexOf {
|
||||
fn name(&self) -> &str {
|
||||
"bytes index-of"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("bytes index-of")
|
||||
.input_output_types(vec![
|
||||
(Type::Binary, Type::Int),
|
||||
(Type::Binary, Type::List(Box::new(Type::Int))),
|
||||
// FIXME: this shouldn't be needed, cell paths should work with the two
|
||||
// above
|
||||
(Type::Table(vec![]), Type::Table(vec![])),
|
||||
(Type::Record(vec![]), Type::Record(vec![])),
|
||||
])
|
||||
.allow_variants_without_examples(true)
|
||||
.required(
|
||||
"pattern",
|
||||
SyntaxShape::Binary,
|
||||
"the pattern to find index of",
|
||||
)
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"for a data structure input, find the indexes at the given cell paths",
|
||||
)
|
||||
.switch("all", "returns all matched index", Some('a'))
|
||||
.switch("end", "search from the end of the binary", Some('e'))
|
||||
.category(Category::Bytes)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Returns start index of first occurrence of pattern in bytes, or -1 if no match."
|
||||
}
|
||||
|
||||
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 cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let arg = Arguments {
|
||||
pattern,
|
||||
end: call.has_flag("end"),
|
||||
all: call.has_flag("all"),
|
||||
cell_paths,
|
||||
};
|
||||
operate(index_of, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Returns index of pattern in bytes",
|
||||
example: " 0x[33 44 55 10 01 13 44 55] | bytes index-of 0x[44 55]",
|
||||
result: Some(Value::test_int(1)),
|
||||
},
|
||||
Example {
|
||||
description: "Returns index of pattern, search from end",
|
||||
example: " 0x[33 44 55 10 01 13 44 55] | bytes index-of -e 0x[44 55]",
|
||||
result: Some(Value::test_int(6)),
|
||||
},
|
||||
Example {
|
||||
description: "Returns all matched index",
|
||||
example: " 0x[33 44 55 10 01 33 44 33 44] | bytes index-of -a 0x[33 44]",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_int(0), Value::test_int(5), Value::test_int(7)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Returns all matched index, searching from end",
|
||||
example: " 0x[33 44 55 10 01 33 44 33 44] | bytes index-of -a -e 0x[33 44]",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_int(7), Value::test_int(5), Value::test_int(0)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Returns index of pattern for specific column",
|
||||
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes index-of 0x[11] ColA ColC"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||
vals: vec![
|
||||
Value::test_int(0),
|
||||
Value::Binary {
|
||||
val: vec![0x14, 0x15, 0x16],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::test_int(-1),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn index_of(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => index_of_impl(val, args, *val_span),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => val.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "binary".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.expect_span(),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn index_of_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||
if arg.all {
|
||||
search_all_index(input, &arg.pattern, arg.end, span)
|
||||
} else {
|
||||
let mut iter = input.windows(arg.pattern.len());
|
||||
|
||||
if arg.end {
|
||||
Value::Int {
|
||||
val: iter
|
||||
.rev()
|
||||
.position(|sub_bytes| sub_bytes == arg.pattern)
|
||||
.map(|x| (input.len() - arg.pattern.len() - x) as i64)
|
||||
.unwrap_or(-1),
|
||||
span,
|
||||
}
|
||||
} else {
|
||||
Value::Int {
|
||||
val: iter
|
||||
.position(|sub_bytes| sub_bytes == arg.pattern)
|
||||
.map(|x| x as i64)
|
||||
.unwrap_or(-1),
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn search_all_index(input: &[u8], pattern: &[u8], from_end: bool, span: Span) -> Value {
|
||||
let mut result = vec![];
|
||||
if from_end {
|
||||
let (mut left, mut right) = (
|
||||
input.len() as isize - pattern.len() as isize,
|
||||
input.len() as isize,
|
||||
);
|
||||
while left >= 0 {
|
||||
if &input[left as usize..right as usize] == pattern {
|
||||
result.push(Value::Int {
|
||||
val: left as i64,
|
||||
span,
|
||||
});
|
||||
left -= pattern.len() as isize;
|
||||
right -= pattern.len() as isize;
|
||||
} else {
|
||||
left -= 1;
|
||||
right -= 1;
|
||||
}
|
||||
}
|
||||
Value::List { vals: result, span }
|
||||
} else {
|
||||
// doing find stuff.
|
||||
let (mut left, mut right) = (0, pattern.len());
|
||||
let input_len = input.len();
|
||||
let pattern_len = pattern.len();
|
||||
while right <= input_len {
|
||||
if &input[left..right] == pattern {
|
||||
result.push(Value::Int {
|
||||
val: left as i64,
|
||||
span,
|
||||
});
|
||||
left += pattern_len;
|
||||
right += pattern_len;
|
||||
} else {
|
||||
left += 1;
|
||||
right += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Value::List { vals: result, span }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(BytesIndexOf {})
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
use nu_cmd_base::input_handler::{operate, CellPathOnlyArgs};
|
||||
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 BytesLen;
|
||||
|
||||
impl Command for BytesLen {
|
||||
fn name(&self) -> &str {
|
||||
"bytes length"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("bytes length")
|
||||
.input_output_types(vec![
|
||||
(Type::Binary, Type::Int),
|
||||
(
|
||||
Type::List(Box::new(Type::Binary)),
|
||||
Type::List(Box::new(Type::Int)),
|
||||
),
|
||||
(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, find the length of data at the given cell paths",
|
||||
)
|
||||
.category(Category::Bytes)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Output the length of any bytes in the pipeline."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["size", "count"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let arg = CellPathOnlyArgs::from(cell_paths);
|
||||
operate(length, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Return the length of a binary",
|
||||
example: "0x[1F FF AA AB] | bytes length",
|
||||
result: Some(Value::test_int(4)),
|
||||
},
|
||||
Example {
|
||||
description: "Return the lengths of multiple binaries",
|
||||
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 length(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => Value::int(val.len() as i64, *val_span),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => val.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "binary".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.expect_span(),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(BytesLen {})
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
mod add;
|
||||
mod at;
|
||||
mod build_;
|
||||
mod bytes_;
|
||||
mod collect;
|
||||
mod ends_with;
|
||||
mod index_of;
|
||||
mod length;
|
||||
mod remove;
|
||||
mod replace;
|
||||
mod reverse;
|
||||
mod starts_with;
|
||||
|
||||
pub use add::BytesAdd;
|
||||
pub use at::BytesAt;
|
||||
pub use build_::BytesBuild;
|
||||
pub use bytes_::Bytes;
|
||||
pub use collect::BytesCollect;
|
||||
pub use ends_with::BytesEndsWith;
|
||||
pub use index_of::BytesIndexOf;
|
||||
pub use length::BytesLen;
|
||||
pub use remove::BytesRemove;
|
||||
pub use replace::BytesReplace;
|
||||
pub use reverse::BytesReverse;
|
||||
pub use starts_with::BytesStartsWith;
|
@ -1,215 +0,0 @@
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
||||
Value,
|
||||
};
|
||||
|
||||
struct Arguments {
|
||||
pattern: Vec<u8>,
|
||||
end: bool,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
all: bool,
|
||||
}
|
||||
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BytesRemove;
|
||||
|
||||
impl Command for BytesRemove {
|
||||
fn name(&self) -> &str {
|
||||
"bytes remove"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("bytes remove")
|
||||
.input_output_types(vec![
|
||||
(Type::Binary, Type::Binary),
|
||||
(Type::Table(vec![]), Type::Table(vec![])),
|
||||
(Type::Record(vec![]), Type::Record(vec![])),
|
||||
])
|
||||
.required("pattern", SyntaxShape::Binary, "the pattern to find")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"for a data structure input, remove bytes from data at the given cell paths",
|
||||
)
|
||||
.switch("end", "remove from end of binary", Some('e'))
|
||||
.switch("all", "remove occurrences of finding binary", Some('a'))
|
||||
.category(Category::Bytes)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Remove bytes."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["search", "shift", "switch"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
|
||||
if pattern_to_remove.item.is_empty() {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: "the pattern to remove cannot be empty".to_string(),
|
||||
span: pattern_to_remove.span,
|
||||
});
|
||||
}
|
||||
|
||||
let pattern_to_remove: Vec<u8> = pattern_to_remove.item;
|
||||
let arg = Arguments {
|
||||
pattern: pattern_to_remove,
|
||||
end: call.has_flag("end"),
|
||||
cell_paths,
|
||||
all: call.has_flag("all"),
|
||||
};
|
||||
|
||||
operate(remove, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Remove contents",
|
||||
example: "0x[10 AA FF AA FF] | bytes remove 0x[10 AA]",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0xFF, 0xAA, 0xFF],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Remove all occurrences of find binary in record field",
|
||||
example: "{ data: 0x[10 AA 10 BB 10] } | bytes remove -a 0x[10] data",
|
||||
result: Some(Value::test_record(vec!["data"],
|
||||
vec![Value::test_binary(vec![0xAA, 0xBB])])),
|
||||
},
|
||||
Example {
|
||||
description: "Remove occurrences of find binary from end",
|
||||
example: "0x[10 AA 10 BB CC AA 10] | bytes remove -e 0x[10]",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0x10, 0xAA, 0x10, 0xBB, 0xCC, 0xAA],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Remove all occurrences of find binary in table",
|
||||
example: "[[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes remove 0x[11] ColA ColC",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||
vals: vec![
|
||||
Value::Binary {
|
||||
val: vec![0x12, 0x13],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Binary {
|
||||
val: vec![0x14, 0x15, 0x16],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Binary {
|
||||
val: vec![0x17, 0x18, 0x19],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn remove(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => remove_impl(val, args, *val_span),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => val.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "binary".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.expect_span(),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||
let mut result = vec![];
|
||||
let remove_all = arg.all;
|
||||
let input_len = input.len();
|
||||
let pattern_len = arg.pattern.len();
|
||||
|
||||
// Note:
|
||||
// remove_all from start and end will generate the same result.
|
||||
// so we'll put `remove_all` relative logic into else clause.
|
||||
if arg.end && !remove_all {
|
||||
let (mut left, mut right) = (
|
||||
input.len() as isize - arg.pattern.len() as isize,
|
||||
input.len() as isize,
|
||||
);
|
||||
while left >= 0 && input[left as usize..right as usize] != arg.pattern {
|
||||
result.push(input[right as usize - 1]);
|
||||
left -= 1;
|
||||
right -= 1;
|
||||
}
|
||||
// append the remaining thing to result, this can be happening when
|
||||
// we have something to remove and remove_all is False.
|
||||
let mut remain = input[..left as usize].iter().copied().rev().collect();
|
||||
result.append(&mut remain);
|
||||
result = result.into_iter().rev().collect();
|
||||
Value::Binary { val: result, span }
|
||||
} else {
|
||||
let (mut left, mut right) = (0, arg.pattern.len());
|
||||
while right <= input_len {
|
||||
if input[left..right] == arg.pattern {
|
||||
left += pattern_len;
|
||||
right += pattern_len;
|
||||
if !remove_all {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
result.push(input[left]);
|
||||
left += 1;
|
||||
right += 1;
|
||||
}
|
||||
}
|
||||
// append the remaining thing to result, this can happened when
|
||||
// we have something to remove and remove_all is False.
|
||||
let mut remain = input[left..].to_vec();
|
||||
result.append(&mut remain);
|
||||
Value::Binary { val: result, span }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(BytesRemove {})
|
||||
}
|
||||
}
|
@ -1,193 +0,0 @@
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
||||
Value,
|
||||
};
|
||||
|
||||
struct Arguments {
|
||||
find: Vec<u8>,
|
||||
replace: Vec<u8>,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
all: bool,
|
||||
}
|
||||
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BytesReplace;
|
||||
|
||||
impl Command for BytesReplace {
|
||||
fn name(&self) -> &str {
|
||||
"bytes replace"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("bytes replace")
|
||||
.input_output_types(vec![
|
||||
(Type::Binary, Type::Binary),
|
||||
(Type::Table(vec![]), Type::Table(vec![])),
|
||||
(Type::Record(vec![]), Type::Record(vec![])),
|
||||
])
|
||||
.allow_variants_without_examples(true)
|
||||
.required("find", SyntaxShape::Binary, "the pattern to find")
|
||||
.required("replace", SyntaxShape::Binary, "the replacement pattern")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"for a data structure input, replace bytes in data at the given cell paths",
|
||||
)
|
||||
.switch("all", "replace all occurrences of find binary", Some('a'))
|
||||
.category(Category::Bytes)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Find and replace binary."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["search", "shift", "switch"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
|
||||
if find.item.is_empty() {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: "the pattern to find cannot be empty".to_string(),
|
||||
span: find.span,
|
||||
});
|
||||
}
|
||||
|
||||
let arg = Arguments {
|
||||
find: find.item,
|
||||
replace: call.req::<Vec<u8>>(engine_state, stack, 1)?,
|
||||
cell_paths,
|
||||
all: call.has_flag("all"),
|
||||
};
|
||||
|
||||
operate(replace, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Find and replace contents",
|
||||
example: "0x[10 AA FF AA FF] | bytes replace 0x[10 AA] 0x[FF]",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0xFF, 0xFF, 0xAA, 0xFF],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Find and replace all occurrences of find binary",
|
||||
example: "0x[10 AA 10 BB 10] | bytes replace -a 0x[10] 0x[A0]",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0xA0, 0xAA, 0xA0, 0xBB, 0xA0],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Find and replace all occurrences of find binary in table",
|
||||
example: "[[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes replace -a 0x[11] 0x[13] ColA ColC",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||
vals: vec![
|
||||
Value::Binary {
|
||||
val: vec![0x13, 0x12, 0x13],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Binary {
|
||||
val: vec![0x14, 0x15, 0x16],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Binary {
|
||||
val: vec![0x17, 0x18, 0x19],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn replace(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => replace_impl(val, args, *val_span),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => val.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "binary".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.expect_span(),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||
let mut replaced = vec![];
|
||||
let replace_all = arg.all;
|
||||
|
||||
// doing find-and-replace stuff.
|
||||
let (mut left, mut right) = (0, arg.find.len());
|
||||
let input_len = input.len();
|
||||
let pattern_len = arg.find.len();
|
||||
while right <= input_len {
|
||||
if input[left..right] == arg.find {
|
||||
let mut to_replace = arg.replace.clone();
|
||||
replaced.append(&mut to_replace);
|
||||
left += pattern_len;
|
||||
right += pattern_len;
|
||||
if !replace_all {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
replaced.push(input[left]);
|
||||
left += 1;
|
||||
right += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut remain = input[left..].to_vec();
|
||||
replaced.append(&mut remain);
|
||||
Value::Binary {
|
||||
val: replaced,
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(BytesReplace {})
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
use nu_cmd_base::input_handler::{operate, CellPathOnlyArgs};
|
||||
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 BytesReverse;
|
||||
|
||||
impl Command for BytesReverse {
|
||||
fn name(&self) -> &str {
|
||||
"bytes reverse"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("bytes reverse")
|
||||
.input_output_types(vec![
|
||||
(Type::Binary, Type::Binary),
|
||||
(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, reverse data at the given cell paths",
|
||||
)
|
||||
.category(Category::Bytes)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Reverse the bytes in the pipeline."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["convert", "inverse", "flip"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let arg = CellPathOnlyArgs::from(cell_paths);
|
||||
operate(reverse, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Reverse bytes `0x[1F FF AA AA]`",
|
||||
example: "0x[1F FF AA AA] | bytes reverse",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0xAA, 0xAA, 0xFF, 0x1F],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Reverse bytes `0x[FF AA AA]`",
|
||||
example: "0x[FF AA AA] | bytes reverse",
|
||||
result: Some(Value::Binary {
|
||||
val: vec![0xAA, 0xAA, 0xFF],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn reverse(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => {
|
||||
let mut reversed_input = val.to_vec();
|
||||
reversed_input.reverse();
|
||||
Value::Binary {
|
||||
val: reversed_input,
|
||||
span: *val_span,
|
||||
}
|
||||
}
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => val.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "binary".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.expect_span(),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(BytesReverse {})
|
||||
}
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
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::IntoPipelineData;
|
||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
||||
|
||||
struct Arguments {
|
||||
pattern: Vec<u8>,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_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")
|
||||
.input_output_types(vec![
|
||||
(Type::Binary, Type::Bool),
|
||||
(Type::Table(vec![]), Type::Table(vec![])),
|
||||
(Type::Record(vec![]), Type::Record(vec![])),
|
||||
])
|
||||
.allow_variants_without_examples(true)
|
||||
.required("pattern", SyntaxShape::Binary, "the pattern to match")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"for a data structure input, check if bytes at the given cell paths start with the pattern",
|
||||
)
|
||||
.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 cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let arg = Arguments {
|
||||
pattern,
|
||||
cell_paths,
|
||||
};
|
||||
|
||||
match input {
|
||||
PipelineData::ExternalStream {
|
||||
stdout: Some(stream),
|
||||
span,
|
||||
..
|
||||
} => {
|
||||
let mut i = 0;
|
||||
|
||||
for item in stream {
|
||||
let byte_slice = match &item {
|
||||
// String and binary data are valid byte patterns
|
||||
Ok(Value::String { val, .. }) => val.as_bytes(),
|
||||
Ok(Value::Binary { val, .. }) => val,
|
||||
// If any Error value is output, echo it back
|
||||
Ok(v @ Value::Error { .. }) => return Ok(v.clone().into_pipeline_data()),
|
||||
// Unsupported data
|
||||
Ok(other) => {
|
||||
return Ok(Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "string and binary".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.expect_span(),
|
||||
}),
|
||||
}
|
||||
.into_pipeline_data());
|
||||
}
|
||||
Err(err) => return Err(err.to_owned()),
|
||||
};
|
||||
|
||||
let max = byte_slice.len().min(arg.pattern.len() - i);
|
||||
|
||||
if byte_slice[..max] == arg.pattern[i..i + max] {
|
||||
i += max;
|
||||
|
||||
if i >= arg.pattern.len() {
|
||||
return Ok(Value::bool(true, span).into_pipeline_data());
|
||||
}
|
||||
} else {
|
||||
return Ok(Value::bool(false, span).into_pipeline_data());
|
||||
}
|
||||
}
|
||||
|
||||
// We reached the end of the stream and never returned,
|
||||
// the pattern wasn't exhausted so it probably doesn't match
|
||||
Ok(Value::bool(false, span).into_pipeline_data())
|
||||
}
|
||||
_ => 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::test_bool(true)),
|
||||
},
|
||||
Example {
|
||||
description: "Checks if binary starts with `0x[1F]`",
|
||||
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F]",
|
||||
result: Some(Value::test_bool(true)),
|
||||
},
|
||||
Example {
|
||||
description: "Checks if binary starts with `0x[1F]`",
|
||||
example: "0x[1F FF AA AA] | bytes starts-with 0x[11]",
|
||||
result: Some(Value::test_bool(false)),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn starts_with(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => Value::bool(val.starts_with(&args.pattern), *val_span),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => val.clone(),
|
||||
other => Value::Error {
|
||||
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "binary".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.expect_span(),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(BytesStartsWith {})
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
mod bits;
|
||||
mod bytes;
|
||||
mod conversions;
|
||||
mod filters;
|
||||
mod formats;
|
||||
@ -7,19 +6,6 @@ mod math;
|
||||
mod platform;
|
||||
mod strings;
|
||||
|
||||
pub use bytes::Bytes;
|
||||
pub use bytes::BytesAdd;
|
||||
pub use bytes::BytesAt;
|
||||
pub use bytes::BytesBuild;
|
||||
pub use bytes::BytesCollect;
|
||||
pub use bytes::BytesEndsWith;
|
||||
pub use bytes::BytesIndexOf;
|
||||
pub use bytes::BytesLen;
|
||||
pub use bytes::BytesRemove;
|
||||
pub use bytes::BytesReplace;
|
||||
pub use bytes::BytesReverse;
|
||||
pub use bytes::BytesStartsWith;
|
||||
|
||||
pub use bits::Bits;
|
||||
pub use bits::BitsAnd;
|
||||
pub use bits::BitsInto;
|
||||
@ -111,22 +97,6 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
|
||||
BitsXor
|
||||
}
|
||||
|
||||
// Bytes
|
||||
bind_command! {
|
||||
Bytes,
|
||||
BytesLen,
|
||||
BytesStartsWith,
|
||||
BytesEndsWith,
|
||||
BytesReverse,
|
||||
BytesReplace,
|
||||
BytesAdd,
|
||||
BytesAt,
|
||||
BytesIndexOf,
|
||||
BytesCollect,
|
||||
BytesRemove,
|
||||
BytesBuild
|
||||
}
|
||||
|
||||
// Math
|
||||
bind_command! {
|
||||
MathArcSin,
|
||||
|
Reference in New Issue
Block a user