Refactor: introduce general operate commands to reduce duplicate code (#6879)

* make format filesize more flexible

* make code simpler

* finish refactor on bytes commands

* finish refactor on str commands

* fimplify code

* rename from column_paths to cell_paths
This commit is contained in:
WindSoilder 2022-10-30 05:29:46 +08:00 committed by GitHub
parent 843d8c2242
commit 457514590d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 902 additions and 1126 deletions

View File

@ -1,4 +1,4 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
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;
@ -10,12 +10,12 @@ struct Arguments {
added_data: Vec<u8>, added_data: Vec<u8>,
index: Option<usize>, index: Option<usize>,
end: bool, end: bool,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -62,12 +62,8 @@ impl Command for BytesAdd {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let added_data: Vec<u8> = call.req(engine_state, stack, 0)?; let added_data: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
None
} else {
Some(column_paths)
};
let index: Option<usize> = call.get_flag(engine_state, stack, "index")?; let index: Option<usize> = call.get_flag(engine_state, stack, "index")?;
let end = call.has_flag("end"); let end = call.has_flag("end");
@ -75,7 +71,7 @@ impl Command for BytesAdd {
added_data, added_data,
index, index,
end, end,
column_paths, cell_paths,
}; };
operate(add, arg, input, call.head, engine_state.ctrlc.clone()) operate(add, arg, input, call.head, engine_state.ctrlc.clone())
} }
@ -118,7 +114,25 @@ impl Command for BytesAdd {
} }
} }
fn add(input: &[u8], args: &Arguments, span: Span) -> Value { fn add(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => add_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn add_impl(input: &[u8], args: &Arguments, span: Span) -> Value {
match args.index { match args.index {
None => { None => {
if args.end { if args.end {

View File

@ -1,4 +1,4 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
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;
@ -15,12 +15,12 @@ struct Arguments {
start: isize, start: isize,
end: isize, end: isize,
arg_span: Span, arg_span: Span,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -141,17 +141,13 @@ impl Command for BytesAt {
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let range: Value = call.req(engine_state, stack, 0)?; let range: Value = call.req(engine_state, stack, 0)?;
let (start, end, arg_span) = parse_range(range, call.head)?; let (start, end, arg_span) = parse_range(range, call.head)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
None
} else {
Some(column_paths)
};
let arg = Arguments { let arg = Arguments {
start, start,
end, end,
arg_span, arg_span,
column_paths, cell_paths,
}; };
operate(at, arg, input, call.head, engine_state.ctrlc.clone()) operate(at, arg, input, call.head, engine_state.ctrlc.clone())
} }
@ -228,7 +224,25 @@ impl Command for BytesAt {
} }
} }
fn at(input: &[u8], arg: &Arguments, span: Span) -> Value { fn at(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => at_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn at_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let len: isize = input.len() as isize; let len: isize = input.len() as isize;
let start: isize = if arg.start < 0 { let start: isize = if arg.start < 0 {

View File

@ -1,4 +1,4 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
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,12 +8,12 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
struct Arguments { struct Arguments {
pattern: Vec<u8>, pattern: Vec<u8>,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -53,15 +53,11 @@ impl Command for BytesEndsWith {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?; let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
None
} else {
Some(column_paths)
};
let arg = Arguments { let arg = Arguments {
pattern, pattern,
column_paths, cell_paths,
}; };
operate(ends_with, arg, input, call.head, engine_state.ctrlc.clone()) operate(ends_with, arg, input, call.head, engine_state.ctrlc.clone())
} }
@ -96,10 +92,24 @@ impl Command for BytesEndsWith {
} }
} }
fn ends_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value { fn ends_with(val: &Value, args: &Arguments, span: Span) -> Value {
Value::Bool { match val {
val: input.ends_with(pattern), Value::Binary {
val,
span: val_span,
} => Value::Bool {
val: val.ends_with(&args.pattern),
span: *val_span,
},
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span, span,
),
},
} }
} }

View File

@ -1,4 +1,4 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::ast::{Call, CellPath}; use nu_protocol::ast::{Call, CellPath};
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
@ -10,12 +10,12 @@ struct Arguments {
pattern: Vec<u8>, pattern: Vec<u8>,
end: bool, end: bool,
all: bool, all: bool,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -60,17 +60,13 @@ impl Command for BytesIndexOf {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?; let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
None
} else {
Some(column_paths)
};
let arg = Arguments { let arg = Arguments {
pattern, pattern,
end: call.has_flag("end"), end: call.has_flag("end"),
all: call.has_flag("all"), all: call.has_flag("all"),
column_paths, cell_paths,
}; };
operate(index_of, arg, input, call.head, engine_state.ctrlc.clone()) operate(index_of, arg, input, call.head, engine_state.ctrlc.clone())
} }
@ -126,7 +122,25 @@ impl Command for BytesIndexOf {
} }
} }
fn index_of(input: &[u8], arg: &Arguments, span: Span) -> Value { 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),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn index_of_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
if arg.all { if arg.all {
search_all_index(input, &arg.pattern, arg.end, span) search_all_index(input, &arg.pattern, arg.end, span)
} else { } else {

View File

@ -1,4 +1,4 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
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;
@ -10,12 +10,12 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
pub struct BytesLen; pub struct BytesLen;
struct Arguments { struct Arguments {
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -49,13 +49,9 @@ impl Command for BytesLen {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
None let arg = Arguments { cell_paths };
} else {
Some(column_paths)
};
let arg = Arguments { column_paths };
operate(length, arg, input, call.head, engine_state.ctrlc.clone()) operate(length, arg, input, call.head, engine_state.ctrlc.clone())
} }
@ -78,10 +74,24 @@ impl Command for BytesLen {
} }
} }
fn length(input: &[u8], _arg: &Arguments, span: Span) -> Value { fn length(val: &Value, _args: &Arguments, span: Span) -> Value {
Value::Int { match val {
val: input.len() as i64, Value::Binary {
val,
span: val_span,
} => Value::Int {
val: val.len() as i64,
span: *val_span,
},
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span, span,
),
},
} }
} }

View File

@ -11,11 +11,6 @@ mod replace;
mod reverse; mod reverse;
mod starts_with; 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 add::BytesAdd; pub use add::BytesAdd;
pub use at::BytesAt; pub use at::BytesAt;
pub use build_::BytesBuild; pub use build_::BytesBuild;
@ -28,71 +23,3 @@ pub use remove::BytesRemove;
pub use replace::BytesReplace; pub use replace::BytesReplace;
pub use reverse::BytesReverse; pub use reverse::BytesReverse;
pub use starts_with::BytesStartsWith; 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,
)
}
}
}

View File

@ -1,4 +1,4 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
@ -9,13 +9,13 @@ use nu_protocol::{
struct Arguments { struct Arguments {
pattern: Vec<u8>, pattern: Vec<u8>,
end: bool, end: bool,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
all: bool, all: bool,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -55,12 +55,8 @@ impl Command for BytesRemove {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
None
} else {
Some(column_paths)
};
let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?; let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
if pattern_to_remove.item.is_empty() { if pattern_to_remove.item.is_empty() {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
@ -73,7 +69,7 @@ impl Command for BytesRemove {
let arg = Arguments { let arg = Arguments {
pattern: pattern_to_remove, pattern: pattern_to_remove,
end: call.has_flag("end"), end: call.has_flag("end"),
column_paths, cell_paths,
all: call.has_flag("all"), all: call.has_flag("all"),
}; };
@ -135,7 +131,25 @@ impl Command for BytesRemove {
} }
} }
fn remove(input: &[u8], arg: &Arguments, span: Span) -> Value { fn remove(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => remove_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let mut result = vec![]; let mut result = vec![];
let remove_all = arg.all; let remove_all = arg.all;
let input_len = input.len(); let input_len = input.len();

View File

@ -1,4 +1,4 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
@ -9,13 +9,13 @@ use nu_protocol::{
struct Arguments { struct Arguments {
find: Vec<u8>, find: Vec<u8>,
replace: Vec<u8>, replace: Vec<u8>,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
all: bool, all: bool,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -55,12 +55,8 @@ impl Command for BytesReplace {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
None
} else {
Some(column_paths)
};
let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?; let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
if find.item.is_empty() { if find.item.is_empty() {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
@ -72,7 +68,7 @@ impl Command for BytesReplace {
let arg = Arguments { let arg = Arguments {
find: find.item, find: find.item,
replace: call.req::<Vec<u8>>(engine_state, stack, 1)?, replace: call.req::<Vec<u8>>(engine_state, stack, 1)?,
column_paths, cell_paths,
all: call.has_flag("all"), all: call.has_flag("all"),
}; };
@ -126,7 +122,25 @@ impl Command for BytesReplace {
} }
} }
fn replace(input: &[u8], arg: &Arguments, span: Span) -> Value { fn replace(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => replace_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn replace_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let mut replaced = vec![]; let mut replaced = vec![];
let replace_all = arg.all; let replace_all = arg.all;

View File

@ -1,4 +1,4 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
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;
@ -7,12 +7,12 @@ use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
struct Arguments { struct Arguments {
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -50,13 +50,9 @@ impl Command for BytesReverse {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
None let arg = Arguments { cell_paths };
} else {
Some(column_paths)
};
let arg = Arguments { column_paths };
operate(reverse, arg, input, call.head, engine_state.ctrlc.clone()) operate(reverse, arg, input, call.head, engine_state.ctrlc.clone())
} }
@ -82,12 +78,28 @@ impl Command for BytesReverse {
} }
} }
fn reverse(input: &[u8], _args: &Arguments, span: Span) -> Value { fn reverse(val: &Value, _args: &Arguments, span: Span) -> Value {
let mut reversed_input = input.to_vec(); match val {
Value::Binary {
val,
span: val_span,
} => {
let mut reversed_input = val.to_vec();
reversed_input.reverse(); reversed_input.reverse();
Value::Binary { Value::Binary {
val: reversed_input, val: reversed_input,
span: *val_span,
}
}
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span, span,
),
},
} }
} }

View File

@ -1,4 +1,4 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
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,12 +8,12 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
struct Arguments { struct Arguments {
pattern: Vec<u8>, pattern: Vec<u8>,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -53,15 +53,11 @@ impl Command for BytesStartsWith {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?; let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
None
} else {
Some(column_paths)
};
let arg = Arguments { let arg = Arguments {
pattern, pattern,
column_paths, cell_paths,
}; };
operate( operate(
starts_with, starts_with,
@ -102,10 +98,24 @@ impl Command for BytesStartsWith {
} }
} }
fn starts_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value { fn starts_with(val: &Value, args: &Arguments, span: Span) -> Value {
Value::Bool { match val {
val: input.starts_with(pattern), Value::Binary {
val,
span: val_span,
} => Value::Bool {
val: val.starts_with(&args.pattern),
span: *val_span,
},
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span, span,
),
},
} }
} }

View File

@ -0,0 +1,51 @@
use nu_protocol::ast::CellPath;
use nu_protocol::{PipelineData, ShellError, Span, Value};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
pub trait CmdArgument {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>>;
}
/// A simple wrapper for `PipelineData::map` method.
///
/// In detail, for each elements, invoking relative `cmd` with `arg`.
///
/// If `arg` tell us that it's column path is not None, only map over data under these columns.
/// Else it will apply each column inside a table.
///
/// The validation of input element should be handle by `cmd` itself.
pub fn operate<C, A>(
cmd: C,
mut arg: A,
input: PipelineData,
span: Span,
ctrlc: Option<Arc<AtomicBool>>,
) -> Result<PipelineData, ShellError>
where
A: CmdArgument + Send + Sync + 'static,
C: Fn(&Value, &A, Span) -> Value + Send + Sync + 'static + Clone + Copy,
{
match arg.take_cell_paths() {
None => input.map(move |v| cmd(&v, &arg, v.span().unwrap_or(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| cmd(old, &opt, old.span().unwrap_or(span))),
);
if let Err(error) = r {
return Value::Error { error };
}
}
v
},
ctrlc,
)
}
}
}

View File

@ -14,6 +14,7 @@ mod filters;
mod formats; mod formats;
mod generators; mod generators;
mod hash; mod hash;
mod input_handler;
mod math; mod math;
mod misc; mod misc;
mod network; mod network;

View File

@ -1,11 +1,22 @@
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::{Call, CellPath};
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
format_filesize, Category, Example, IntoPipelineData, PipelineData, PipelineMetadata, format_filesize, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape,
ShellError, Signature, Span, SyntaxShape, Value, Value,
}; };
use std::iter;
struct Arguments {
format_value: String,
cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct FileSize; pub struct FileSize;
@ -17,16 +28,16 @@ impl Command for FileSize {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("format filesize") Signature::build("format filesize")
.required(
"field",
SyntaxShape::String,
"the name of the column to update",
)
.required( .required(
"format value", "format value",
SyntaxShape::String, SyntaxShape::String,
"the format into which convert the file sizes", "the format into which convert the file sizes",
) )
.rest(
"rest",
SyntaxShape::CellPath,
"optinally find and replace text by column paths",
)
.category(Category::Strings) .category(Category::Strings)
} }
@ -45,98 +56,62 @@ impl Command for FileSize {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let field = call.req::<Value>(engine_state, stack, 0)?.as_string()?;
let format_value = call let format_value = call
.req::<Value>(engine_state, stack, 1)? .req::<Value>(engine_state, stack, 0)?
.as_string()? .as_string()?
.to_ascii_lowercase(); .to_ascii_lowercase();
let span = call.head; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let input_metadata = input.metadata(); let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let data_as_value = input.into_value(span); let arg = Arguments {
format_value,
// Something need to consider: cell_paths,
// 1. what if input data type is not table? For now just output nothing. };
// 2. what if value is not a FileSize type? For now just return nothing too for the value. operate(
match data_as_value { format_value_impl,
Value::List { vals, span } => { arg,
format_impl(vals, field, format_value, span, input_metadata) input,
} call.head,
_ => Ok(Value::Nothing { span }.into_pipeline_data()), engine_state.ctrlc.clone(),
} )
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
description: "Convert the size row to KB", description: "Convert the size row to KB",
example: "ls | format filesize size KB", example: "ls | format filesize KB size",
result: None, result: None,
}, },
Example { Example {
description: "Convert the apparent row to B", description: "Convert the apparent row to B",
example: "du | format filesize apparent B", example: "du | format filesize B apparent",
result: None,
},
Example {
description: "Convert the size data to MB",
example: "4Gb | format filesize MB",
result: None, result: None,
}, },
] ]
} }
} }
fn format_impl( fn format_value_impl(val: &Value, arg: &Arguments, span: Span) -> Value {
vals: Vec<Value>,
field: String,
format_value: String,
input_span: Span,
input_metadata: Option<PipelineMetadata>,
) -> Result<PipelineData, ShellError> {
let records: Vec<Value> = vals
.into_iter()
.map(|rec| {
let record_span = rec.span();
match rec {
Value::Record { cols, vals, span } => {
let mut new_cols = vec![];
let mut new_vals = vec![];
for (c, v) in iter::zip(cols, vals) {
// find column to format, try format the value.
if c == field {
new_vals.push(format_value_impl(v, &format_value, span));
} else {
new_vals.push(v);
}
new_cols.push(c);
}
Value::Record {
cols: new_cols,
vals: new_vals,
span,
}
}
_ => Value::Nothing {
span: match record_span {
Ok(s) => s,
Err(_) => input_span,
},
},
}
})
.collect();
let result = Value::List {
vals: records,
span: input_span,
}
.into_pipeline_data();
Ok(result.set_metadata(input_metadata))
}
fn format_value_impl(val: Value, format_value: &str, span: Span) -> Value {
match val { match val {
Value::Filesize { val, span } => Value::String { Value::Filesize { val, span } => Value::String {
// don't need to concern about metric, we just format units by what user input. // don't need to concern about metric, we just format units by what user input.
val: format_filesize(val, format_value, false), val: format_filesize(*val, &arg.format_value, false),
span, span: *span,
},
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is not supported, support type: <filesize>, current_type: {}",
other.get_type()
),
span,
),
}, },
_ => Value::Nothing { span },
} }
} }

View File

@ -22,10 +22,22 @@ pub use upcase::SubCommand as StrUpcase;
use nu_engine::CallExt; use nu_engine::CallExt;
use crate::input_handler::{operate as general_operate, CmdArgument};
use nu_protocol::ast::{Call, CellPath}; use nu_protocol::ast::{Call, CellPath};
use nu_protocol::engine::{EngineState, Stack}; use nu_protocol::engine::{EngineState, Stack};
use nu_protocol::{PipelineData, ShellError, Span, Value}; use nu_protocol::{PipelineData, ShellError, Span, Value};
struct Arguments<F: Fn(&str) -> String + Send + Sync + 'static> {
case_operation: &'static F,
cell_paths: Option<Vec<CellPath>>,
}
impl<F: Fn(&str) -> String + Send + Sync + 'static> CmdArgument for Arguments<F> {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
pub fn operate<F>( pub fn operate<F>(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
@ -36,35 +48,20 @@ pub fn operate<F>(
where where
F: Fn(&str) -> String + Send + Sync + 'static, F: Fn(&str) -> String + Send + Sync + 'static,
{ {
let head = call.head; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?; let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let args = Arguments {
input.map( case_operation,
move |v| { cell_paths,
if column_paths.is_empty() { };
action(&v, case_operation, head) general_operate(action, args, input, call.head, engine_state.ctrlc.clone())
} else {
let mut ret = v;
for path in &column_paths {
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, case_operation, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
} }
pub fn action<F>(input: &Value, case_operation: &F, head: Span) -> Value fn action<F>(input: &Value, args: &Arguments<F>, head: Span) -> Value
where where
F: Fn(&str) -> String + Send + Sync + 'static, F: Fn(&str) -> String + Send + Sync + 'static,
{ {
let case_operation = args.case_operation;
match input { match input {
Value::String { val, .. } => Value::String { Value::String { val, .. } => Value::String {
val: case_operation(val), val: case_operation(val),

View File

@ -1,15 +1,27 @@
use crate::input_handler::{operate, CmdArgument};
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;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::{ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
};
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct SubCommand;
struct Arguments {
substring: String,
cell_paths: Option<Vec<CellPath>>,
case_insensitive: bool,
not_contain: bool,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
impl Command for SubCommand { impl Command for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"str contains" "str contains"
@ -43,7 +55,15 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let args = Arguments {
substring: call.req::<String>(engine_state, stack, 0)?,
cell_paths,
case_insensitive: call.has_flag("insensitive"),
not_contain: call.has_flag("not"),
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -175,53 +195,21 @@ impl Command for SubCommand {
} }
} }
fn operate(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let substring: Spanned<String> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let case_insensitive = call.has_flag("insensitive");
let not_contain = call.has_flag("not");
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, case_insensitive, not_contain, &substring.item, head)
} else {
let mut ret = v;
for path in &column_paths {
let p = substring.item.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, case_insensitive, not_contain, &p, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action( fn action(
input: &Value, input: &Value,
case_insensitive: bool, Arguments {
not_contain: bool, case_insensitive,
substring: &str, not_contain,
substring,
..
}: &Arguments,
head: Span, head: Span,
) -> Value { ) -> Value {
match input { match input {
Value::String { val, .. } => Value::Bool { Value::String { val, .. } => Value::Bool {
val: match case_insensitive { val: match case_insensitive {
true => { true => {
if not_contain { if *not_contain {
!val.to_lowercase() !val.to_lowercase()
.contains(substring.to_lowercase().as_str()) .contains(substring.to_lowercase().as_str())
} else { } else {
@ -230,7 +218,7 @@ fn action(
} }
} }
false => { false => {
if not_contain { if *not_contain {
!val.contains(substring) !val.contains(substring)
} else { } else {
val.contains(substring) val.contains(substring)

View File

@ -1,14 +1,26 @@
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
levenshtein_distance, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, levenshtein_distance, Category, Example, PipelineData, ShellError, Signature, Span,
SyntaxShape, Value, SyntaxShape, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct SubCommand;
struct Arguments {
compare_string: String,
cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
impl Command for SubCommand { impl Command for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"str distance" "str distance"
@ -44,7 +56,14 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let compare_string: String = 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(|| cell_paths);
let args = Arguments {
compare_string,
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -63,40 +82,8 @@ impl Command for SubCommand {
} }
} }
fn operate( fn action(input: &Value, args: &Arguments, head: Span) -> Value {
engine_state: &EngineState, let compare_string = &args.compare_string;
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let compare_string: Spanned<String> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, &compare_string.item, head)
} else {
let mut ret = v;
for path in &column_paths {
let c = compare_string.item.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &c, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action(input: &Value, compare_string: &str, head: Span) -> Value {
match &input { match &input {
Value::String { val, .. } => { Value::String { val, .. } => {
let distance = levenshtein_distance(val, compare_string); let distance = levenshtein_distance(val, compare_string);

View File

@ -1,11 +1,22 @@
use crate::input_handler::{operate, CmdArgument};
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;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::Spanned;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
struct Arguments {
substring: String,
cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct SubCommand;
@ -40,7 +51,13 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let args = Arguments {
substring: call.req::<String>(engine_state, stack, 0)?,
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -65,43 +82,10 @@ impl Command for SubCommand {
} }
} }
fn operate( fn action(input: &Value, args: &Arguments, head: Span) -> Value {
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let substring: Spanned<String> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, &substring.item, head)
} else {
let mut ret = v;
for path in &column_paths {
let p = substring.item.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &p, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action(input: &Value, substring: &str, head: Span) -> Value {
match input { match input {
Value::String { val, .. } => Value::Bool { Value::String { val, .. } => Value::Bool {
val: val.ends_with(substring), val: val.ends_with(&args.substring),
span: head, span: head,
}, },
other => Value::Error { other => Value::Error {

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
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;
@ -5,13 +6,18 @@ use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::Spanned; use nu_protocol::Spanned;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
use std::sync::Arc;
struct Arguments { struct Arguments {
end: bool, end: bool,
substring: String, substring: String,
range: Option<Value>, range: Option<Value>,
column_paths: Vec<CellPath>, cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -58,7 +64,16 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let substring: Spanned<String> = 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(|| cell_paths);
let args = Arguments {
substring: substring.item,
range: call.get_flag(engine_state, stack, "range")?,
end: call.has_flag("end"),
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -97,44 +112,6 @@ impl Command for SubCommand {
} }
} }
fn operate(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let substring: Spanned<String> = call.req(engine_state, stack, 0)?;
let options = Arc::new(Arguments {
substring: substring.item,
range: call.get_flag(engine_state, stack, "range")?,
end: call.has_flag("end"),
column_paths: call.rest(engine_state, stack, 1)?,
});
let head = call.head;
input.map(
move |v| {
if options.column_paths.is_empty() {
action(&v, &options, head)
} else {
let mut ret = v;
for path in &options.column_paths {
let opt = options.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &opt, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action( fn action(
input: &Value, input: &Value,
Arguments { Arguments {
@ -294,7 +271,7 @@ mod tests {
val: String::from(""), val: String::from(""),
span: Span::test_data(), span: Span::test_data(),
}), }),
column_paths: vec![], cell_paths: None,
end: false, end: false,
}; };
@ -316,7 +293,7 @@ mod tests {
val: String::from(""), val: String::from(""),
span: Span::test_data(), span: Span::test_data(),
}), }),
column_paths: vec![], cell_paths: None,
end: false, end: false,
}; };
@ -339,7 +316,7 @@ mod tests {
val: String::from("1"), val: String::from("1"),
span: Span::test_data(), span: Span::test_data(),
}), }),
column_paths: vec![], cell_paths: None,
end: false, end: false,
}; };
@ -361,7 +338,7 @@ mod tests {
val: String::from(",5"), val: String::from(",5"),
span: Span::test_data(), span: Span::test_data(),
}), }),
column_paths: vec![], cell_paths: None,
end: false, end: false,
}; };
@ -383,7 +360,7 @@ mod tests {
val: String::from("2,6"), val: String::from("2,6"),
span: Span::test_data(), span: Span::test_data(),
}), }),
column_paths: vec![], cell_paths: None,
end: false, end: false,
}; };
@ -405,7 +382,7 @@ mod tests {
val: String::from("2,4"), val: String::from("2,4"),
span: Span::test_data(), span: Span::test_data(),
}), }),
column_paths: vec![], cell_paths: None,
end: false, end: false,
}; };

View File

@ -1,13 +1,23 @@
use crate::input_handler::{operate, CmdArgument};
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;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct SubCommand;
struct Arguments {
cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
impl Command for SubCommand { impl Command for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"str length" "str length"
@ -38,7 +48,10 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let args = Arguments { cell_paths };
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -60,35 +73,7 @@ impl Command for SubCommand {
} }
} }
fn operate( fn action(input: &Value, _arg: &Arguments, head: Span) -> Value {
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)?;
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 { match input {
Value::String { val, .. } => Value::Int { Value::String { val, .. } => Value::Int {
val: val.len() as i64, val: val.len() as i64,

View File

@ -1,15 +1,21 @@
use crate::input_handler::{operate, CmdArgument};
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;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
use std::sync::Arc;
struct Arguments { struct Arguments {
length: Option<i64>, length: Option<i64>,
character: Option<String>, character: Option<String>,
column_paths: Vec<CellPath>, cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -52,7 +58,21 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let args = Arguments {
length: call.get_flag(engine_state, stack, "length")?,
character: call.get_flag(engine_state, stack, "character")?,
cell_paths,
};
if args.length.expect("this exists") < 0 {
return Err(ShellError::UnsupportedInput(
String::from("The length of the string cannot be negative"),
call.head,
));
}
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -93,49 +113,6 @@ impl Command for SubCommand {
} }
} }
fn operate(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let options = Arc::new(Arguments {
length: call.get_flag(engine_state, stack, "length")?,
character: call.get_flag(engine_state, stack, "character")?,
column_paths: call.rest(engine_state, stack, 0)?,
});
if options.length.expect("this exists") < 0 {
return Err(ShellError::UnsupportedInput(
String::from("The length of the string cannot be negative"),
call.head,
));
}
let head = call.head;
input.map(
move |v| {
if options.column_paths.is_empty() {
action(&v, &options, head)
} else {
let mut ret = v;
for path in &options.column_paths {
let opt = options.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &opt, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action( fn action(
input: &Value, input: &Value,
Arguments { Arguments {

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
use fancy_regex::{NoExpand, Regex}; use fancy_regex::{NoExpand, Regex};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
@ -5,17 +6,22 @@ use nu_protocol::{
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
}; };
use std::sync::Arc;
struct Arguments { struct Arguments {
all: bool, all: bool,
find: Spanned<String>, find: Spanned<String>,
replace: Spanned<String>, replace: Spanned<String>,
column_paths: Vec<CellPath>, cell_paths: Option<Vec<CellPath>>,
literal_replace: bool, literal_replace: bool,
no_regex: bool, no_regex: bool,
} }
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct SubCommand;
@ -62,7 +68,22 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let find: Spanned<String> = call.req(engine_state, stack, 0)?;
let replace: Spanned<String> = call.req(engine_state, stack, 1)?;
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let literal_replace = call.has_flag("no-expand");
let no_regex = call.has_flag("string");
let args = Arguments {
all: call.has_flag("all"),
find,
replace,
cell_paths,
literal_replace,
no_regex,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -154,50 +175,6 @@ impl Command for SubCommand {
} }
} }
fn operate(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let find: Spanned<String> = call.req(engine_state, stack, 0)?;
let replace: Spanned<String> = call.req(engine_state, stack, 1)?;
let literal_replace = call.has_flag("no-expand");
let no_regex = call.has_flag("string");
let options = Arc::new(Arguments {
all: call.has_flag("all"),
find,
replace,
column_paths: call.rest(engine_state, stack, 2)?,
literal_replace,
no_regex,
});
input.map(
move |v| {
if options.column_paths.is_empty() {
action(&v, &options, head)
} else {
let mut ret = v;
for path in &options.column_paths {
let opt = options.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &opt, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
struct FindReplace<'a>(&'a str, &'a str); struct FindReplace<'a>(&'a str, &'a str);
fn action( fn action(
@ -305,7 +282,7 @@ mod tests {
let options = Arguments { let options = Arguments {
find: test_spanned_string("Cargo.(.+)"), find: test_spanned_string("Cargo.(.+)"),
replace: test_spanned_string("Carga.$1"), replace: test_spanned_string("Carga.$1"),
column_paths: vec![], cell_paths: None,
literal_replace: false, literal_replace: false,
all: false, all: false,
no_regex: false, no_regex: false,

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
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 SubCommand; pub struct SubCommand;
struct Arguments {
cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
impl Command for SubCommand { impl Command for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"str reverse" "str reverse"
@ -38,7 +49,10 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let args = Arguments { cell_paths };
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -76,35 +90,7 @@ impl Command for SubCommand {
} }
} }
fn operate( fn action(input: &Value, _arg: &Arguments, head: Span) -> Value {
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)?;
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 { match input {
Value::String { val, .. } => Value::String { Value::String { val, .. } => Value::String {
val: val.chars().rev().collect::<String>(), val: val.chars().rev().collect::<String>(),

View File

@ -1,15 +1,21 @@
use crate::input_handler::{operate, CmdArgument};
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;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
use std::sync::Arc;
struct Arguments { struct Arguments {
length: Option<i64>, length: Option<i64>,
character: Option<String>, character: Option<String>,
column_paths: Vec<CellPath>, cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -52,7 +58,21 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let args = Arguments {
length: call.get_flag(engine_state, stack, "length")?,
character: call.get_flag(engine_state, stack, "character")?,
cell_paths,
};
if args.length.expect("this exists") < 0 {
return Err(ShellError::UnsupportedInput(
String::from("The length of the string cannot be negative"),
call.head,
));
}
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -93,49 +113,6 @@ impl Command for SubCommand {
} }
} }
fn operate(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let options = Arc::new(Arguments {
length: call.get_flag(engine_state, stack, "length")?,
character: call.get_flag(engine_state, stack, "character")?,
column_paths: call.rest(engine_state, stack, 0)?,
});
if options.length.expect("this exists") < 0 {
return Err(ShellError::UnsupportedInput(
String::from("The length of the string cannot be negative"),
call.head,
));
}
let head = call.head;
input.map(
move |v| {
if options.column_paths.is_empty() {
action(&v, &options, head)
} else {
let mut ret = v;
for path in &options.column_paths {
let opt = options.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &opt, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action( fn action(
input: &Value, input: &Value,
Arguments { Arguments {

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
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;
@ -5,11 +6,16 @@ use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::Spanned; use nu_protocol::Spanned;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
use std::sync::Arc;
struct Arguments { struct Arguments {
substring: String, substring: String,
column_paths: Vec<CellPath>, cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -47,7 +53,14 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let substring: Spanned<String> = 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(|| cell_paths);
let args = Arguments {
substring: substring.item,
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -80,42 +93,6 @@ impl Command for SubCommand {
} }
} }
fn operate(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let substring: Spanned<String> = call.req(engine_state, stack, 0)?;
let options = Arc::new(Arguments {
substring: substring.item,
column_paths: call.rest(engine_state, stack, 1)?,
});
let head = call.head;
input.map(
move |v| {
if options.column_paths.is_empty() {
action(&v, &options, head)
} else {
let mut ret = v;
for path in &options.column_paths {
let opt = options.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &opt, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action(input: &Value, Arguments { substring, .. }: &Arguments, head: Span) -> Value { fn action(input: &Value, Arguments { substring, .. }: &Arguments, head: Span) -> Value {
match input { match input {
Value::String { val: s, .. } => { Value::String { val: s, .. } => {

View File

@ -1,17 +1,23 @@
use crate::input_handler::{operate, CmdArgument};
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;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::sync::Arc;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct SubCommand;
struct Arguments { struct Arguments {
range: Value, indexes: Substring,
column_paths: Vec<CellPath>, cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -59,7 +65,15 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let range = call.req(engine_state, stack, 0)?;
let indexes: Substring = process_arguments(&range, call.head)?.into();
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let args = Arguments {
indexes,
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -99,44 +113,8 @@ impl Command for SubCommand {
} }
} }
fn operate( fn action(input: &Value, args: &Arguments, head: Span) -> Value {
engine_state: &EngineState, let options = &args.indexes;
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let options = Arc::new(Arguments {
range: call.req(engine_state, stack, 0)?,
column_paths: call.rest(engine_state, stack, 1)?,
});
let head = call.head;
let indexes: Arc<Substring> = Arc::new(process_arguments(&options, head)?.into());
input.map(
move |v| {
if options.column_paths.is_empty() {
action(&v, &indexes, head)
} else {
let mut ret = v;
for path in &options.column_paths {
let indexes = indexes.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &indexes, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action(input: &Value, options: &Substring, head: Span) -> Value {
match input { match input {
Value::String { val: s, .. } => { Value::String { val: s, .. } => {
let len: isize = s.len() as isize; let len: isize = s.len() as isize;
@ -203,8 +181,8 @@ fn action(input: &Value, options: &Substring, head: Span) -> Value {
} }
} }
fn process_arguments(options: &Arguments, head: Span) -> Result<(isize, isize), ShellError> { fn process_arguments(range: &Value, head: Span) -> Result<(isize, isize), ShellError> {
let search = match &options.range { let search = match range {
Value::Range { val, .. } => { Value::Range { val, .. } => {
let start = val.from()?; let start = val.from()?;
let end = val.to()?; let end = val.to()?;
@ -357,7 +335,14 @@ mod tests {
for expectation in &cases { for expectation in &cases {
let expected = expectation.expected; let expected = expectation.expected;
let actual = action(&word, &expectation.options(), Span::test_data()); let actual = action(
&word,
&super::Arguments {
indexes: expectation.options(),
cell_paths: None,
},
Span::test_data(),
);
assert_eq!( assert_eq!(
actual, actual,

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
use fancy_regex::Regex; use fancy_regex::Regex;
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
@ -10,8 +11,16 @@ use nu_protocol::{
pub struct SubCommand; pub struct SubCommand;
struct Arguments { struct Arguments {
character: Option<Spanned<String>>, to_trim: Option<char>,
column_paths: Vec<CellPath>, closure_flags: ClosureFlags,
cell_paths: Option<Vec<CellPath>>,
mode: ActionMode,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
} }
#[derive(Default, Debug, Copy, Clone)] #[derive(Default, Debug, Copy, Clone)]
@ -74,7 +83,45 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
operate(engine_state, stack, call, input, &trim) let character = call.get_flag::<Spanned<String>>(engine_state, stack, "char")?;
let to_trim = match character.as_ref() {
Some(v) => {
if v.item.chars().count() > 1 {
return Err(ShellError::GenericError(
"Trim only works with single character".into(),
"needs single character".into(),
Some(v.span),
None,
Vec::new(),
));
}
v.item.chars().next()
}
None => None,
};
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let mode = match cell_paths {
None => ActionMode::Global,
Some(_) => ActionMode::Local,
};
let args = Arguments {
to_trim,
closure_flags: ClosureFlags {
all_flag: call.has_flag("all"),
left_trim: call.has_flag("left"),
right_trim: call.has_flag("right"),
format_flag: call.has_flag("format"),
both_flag: call.has_flag("both")
|| (!call.has_flag("all")
&& !call.has_flag("left")
&& !call.has_flag("right")
&& !call.has_flag("format")), // this is the case if no flags are provided
},
cell_paths,
mode,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -118,118 +165,25 @@ impl Command for SubCommand {
} }
} }
pub fn operate<F>(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
trim_operation: &'static F,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError>
where
F: Fn(&str, Option<char>, &ClosureFlags) -> String + Send + Sync + 'static,
{
let head = call.head;
let (options, closure_flags, input) = (
Arguments {
character: call.get_flag(engine_state, stack, "char")?,
column_paths: call.rest(engine_state, stack, 0)?,
},
ClosureFlags {
all_flag: call.has_flag("all"),
left_trim: call.has_flag("left"),
right_trim: call.has_flag("right"),
format_flag: call.has_flag("format"),
both_flag: call.has_flag("both")
|| (!call.has_flag("all")
&& !call.has_flag("left")
&& !call.has_flag("right")
&& !call.has_flag("format")), // this is the case if no flags are provided
},
input,
);
let to_trim = match options.character.as_ref() {
Some(v) => {
if v.item.chars().count() > 1 {
return Err(ShellError::GenericError(
"Trim only works with single character".into(),
"needs single character".into(),
Some(v.span),
None,
Vec::new(),
));
}
v.item.chars().next()
}
None => None,
};
input.map(
move |v| {
if options.column_paths.is_empty() {
action(
&v,
head,
to_trim,
&closure_flags,
&trim_operation,
ActionMode::Global,
)
} else {
let mut ret = v;
for path in &options.column_paths {
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| {
action(
old,
head,
to_trim,
&closure_flags,
&trim_operation,
ActionMode::Local,
)
}),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ActionMode { pub enum ActionMode {
Local, Local,
Global, Global,
} }
pub fn action<F>( fn action(input: &Value, arg: &Arguments, head: Span) -> Value {
input: &Value, let char_ = arg.to_trim;
head: Span, let closure_flags = &arg.closure_flags;
char_: Option<char>, let mode = &arg.mode;
closure_flags: &ClosureFlags,
trim_operation: &F,
mode: ActionMode,
) -> Value
where
F: Fn(&str, Option<char>, &ClosureFlags) -> String + Send + Sync + 'static,
{
match input { match input {
Value::String { val: s, .. } => Value::String { Value::String { val: s, .. } => Value::String {
val: trim_operation(s, char_, closure_flags), val: trim(s, char_, closure_flags),
span: head, span: head,
}, },
other => match mode { other => match mode {
ActionMode::Global => match other { ActionMode::Global => match other {
Value::Record { cols, vals, span } => { Value::Record { cols, vals, span } => {
let new_vals = vals let new_vals = vals.iter().map(|v| action(v, arg, head)).collect();
.iter()
.map(|v| action(v, head, char_, closure_flags, trim_operation, mode))
.collect();
Value::Record { Value::Record {
cols: cols.to_vec(), cols: cols.to_vec(),
@ -238,10 +192,7 @@ where
} }
} }
Value::List { vals, span } => { Value::List { vals, span } => {
let new_vals = vals let new_vals = vals.iter().map(|v| action(v, arg, head)).collect();
.iter()
.map(|v| action(v, head, char_, closure_flags, trim_operation, mode))
.collect();
Value::List { Value::List {
vals: new_vals, vals: new_vals,
@ -374,14 +325,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&word, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Local,
&trim, };
ActionMode::Local, let actual = action(&word, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -393,15 +343,13 @@ mod tests {
both_flag: true, both_flag: true,
..Default::default() ..Default::default()
}; };
let args = Arguments {
let actual = action( to_trim: None,
&word, closure_flags,
Span::test_data(), cell_paths: None,
None, mode: ActionMode::Global,
&closure_flags, };
&trim, let actual = action(&word, &args, Span::test_data());
ActionMode::Global,
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -413,15 +361,14 @@ mod tests {
both_flag: true, both_flag: true,
..Default::default() ..Default::default()
}; };
let args = Arguments {
to_trim: None,
closure_flags,
cell_paths: None,
mode: ActionMode::Global,
};
let actual = action( let actual = action(&number, &args, Span::test_data());
&number,
Span::test_data(),
None,
&closure_flags,
&trim,
ActionMode::Global,
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -435,14 +382,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&row, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&row, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -455,14 +401,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&row, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&row, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -475,14 +420,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&word, to_trim: Some('!'),
Span::test_data(), closure_flags,
Some('!'), cell_paths: None,
&closure_flags, mode: ActionMode::Local,
&trim, };
ActionMode::Local, let actual = action(&word, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
@ -494,14 +438,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&word, to_trim: Some(' '),
Span::test_data(), closure_flags,
Some(' '), cell_paths: None,
&closure_flags, mode: ActionMode::Local,
&trim, };
ActionMode::Local, let actual = action(&word, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -517,15 +460,13 @@ mod tests {
all_flag: true, all_flag: true,
..Default::default() ..Default::default()
}; };
let args = Arguments {
let actual = action( to_trim: None,
&row, closure_flags,
Span::test_data(), cell_paths: None,
None, mode: ActionMode::Global,
&closure_flags, };
&trim, let actual = action(&row, &args, Span::test_data());
ActionMode::Global,
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -571,14 +512,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&row, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&row, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -591,14 +531,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&word, to_trim: Some('.'),
Span::test_data(), closure_flags,
Some('.'), cell_paths: None,
&closure_flags, mode: ActionMode::Local,
&trim, };
ActionMode::Local, let actual = action(&word, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -611,14 +550,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&row, to_trim: Some('!'),
Span::test_data(), closure_flags,
Some('!'), cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&row, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -664,14 +602,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&row, to_trim: Some('#'),
Span::test_data(), closure_flags,
Some('#'), cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&row, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -684,14 +621,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&word, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Local,
&trim, };
ActionMode::Local, let actual = action(&word, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -704,14 +640,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&number, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&number, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -724,14 +659,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&word, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&word, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -744,14 +678,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&row, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&row, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -796,15 +729,13 @@ mod tests {
left_trim: true, left_trim: true,
..Default::default() ..Default::default()
}; };
let args = Arguments {
let actual = action( to_trim: None,
&row, closure_flags,
Span::test_data(), cell_paths: None,
None, mode: ActionMode::Global,
&closure_flags, };
&trim, let actual = action(&row, &args, Span::test_data());
ActionMode::Global,
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -817,14 +748,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&word, to_trim: Some('!'),
Span::test_data(), closure_flags,
Some('!'), cell_paths: None,
&closure_flags, mode: ActionMode::Local,
&trim, };
ActionMode::Local, let actual = action(&word, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
@ -835,15 +765,13 @@ mod tests {
right_trim: true, right_trim: true,
..Default::default() ..Default::default()
}; };
let args = Arguments {
let actual = action( to_trim: None,
&word, closure_flags,
Span::test_data(), cell_paths: None,
None, mode: ActionMode::Local,
&closure_flags, };
&trim, let actual = action(&word, &args, Span::test_data());
ActionMode::Local,
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -855,15 +783,13 @@ mod tests {
right_trim: true, right_trim: true,
..Default::default() ..Default::default()
}; };
let args = Arguments {
let actual = action( to_trim: None,
&word, closure_flags,
Span::test_data(), cell_paths: None,
None, mode: ActionMode::Global,
&closure_flags, };
&trim, let actual = action(&word, &args, Span::test_data());
ActionMode::Global,
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -876,14 +802,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&number, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&number, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -896,14 +821,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&row, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&row, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -949,14 +873,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&row, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&row, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -969,14 +892,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&word, to_trim: Some('#'),
Span::test_data(), closure_flags,
Some('#'), cell_paths: None,
&closure_flags, mode: ActionMode::Local,
&trim, };
ActionMode::Local, let actual = action(&word, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -989,14 +911,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&word, to_trim: Some(' '),
Span::test_data(), closure_flags,
Some(' '), cell_paths: None,
&closure_flags, mode: ActionMode::Local,
&trim, };
ActionMode::Local, let actual = action(&word, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -1008,15 +929,13 @@ mod tests {
format_flag: true, format_flag: true,
..Default::default() ..Default::default()
}; };
let args = Arguments {
let actual = action( to_trim: Some(' '),
&word, closure_flags,
Span::test_data(), cell_paths: None,
Some(' '), mode: ActionMode::Local,
&closure_flags, };
&trim, let actual = action(&word, &args, Span::test_data());
ActionMode::Local,
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
@ -1028,14 +947,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&number, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&number, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -1048,14 +966,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&row, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&row, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -1101,14 +1018,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&row, to_trim: None,
Span::test_data(), closure_flags,
None, cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&row, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -1121,14 +1037,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&word, to_trim: Some('.'),
Span::test_data(), closure_flags,
Some('.'), cell_paths: None,
&closure_flags, mode: ActionMode::Local,
&trim, };
ActionMode::Local, let actual = action(&word, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -1142,14 +1057,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&word, to_trim: Some(' '),
Span::test_data(), closure_flags,
Some(' '), cell_paths: None,
&closure_flags, mode: ActionMode::Local,
&trim, };
ActionMode::Local, let actual = action(&word, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -1163,14 +1077,13 @@ mod tests {
..Default::default() ..Default::default()
}; };
let actual = action( let args = Arguments {
&word, to_trim: Some(' '),
Span::test_data(), closure_flags,
Some(' '), cell_paths: None,
&closure_flags, mode: ActionMode::Global,
&trim, };
ActionMode::Global, let actual = action(&word, &args, Span::test_data());
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
} }

View File

@ -55,7 +55,7 @@ fn format_filesize_works() {
cwd: dirs.test(), pipeline( cwd: dirs.test(), pipeline(
r#" r#"
ls ls
| format filesize size KB | format filesize KB size
| get size | get size
| first | first
"# "#
@ -80,7 +80,7 @@ fn format_filesize_works_with_nonempty_files() {
let actual = nu!( let actual = nu!(
cwd: dirs.test(), cwd: dirs.test(),
"ls sample.toml | format filesize size B | get size | first" "ls sample.toml | format filesize B size | get size | first"
); );
#[cfg(not(windows))] #[cfg(not(windows))]