Refactor: finish refactor on commands which take optional cell paths. (#6961)

* refactor on conversions module

* finish refactor on strings command

* simplify code

* rename from ArgumentsCp to CellPathOnlyArgs

* fmt code

* refactor on hash relative commands
This commit is contained in:
WindSoilder 2022-11-01 19:40:11 +08:00 committed by GitHub
parent 1d95861a09
commit e46d610f77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 475 additions and 535 deletions

View File

@ -1,4 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
@ -9,16 +9,6 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
#[derive(Clone)]
pub struct BytesLen;
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 BytesLen {
fn name(&self) -> &str {
"bytes length"
@ -50,8 +40,7 @@ impl Command for BytesLen {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let arg = Arguments { cell_paths };
let arg = CellPathOnlyArgs::from(cell_paths);
operate(length, arg, input, call.head, engine_state.ctrlc.clone())
}
@ -74,7 +63,7 @@ impl Command for BytesLen {
}
}
fn length(val: &Value, _args: &Arguments, span: Span) -> Value {
fn length(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match val {
Value::Binary {
val,

View File

@ -1,4 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
@ -6,16 +6,6 @@ use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
struct Arguments {
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 BytesReverse;
@ -51,8 +41,7 @@ impl Command for BytesReverse {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let arg = Arguments { cell_paths };
let arg = CellPathOnlyArgs::from(cell_paths);
operate(reverse, arg, input, call.head, engine_state.ctrlc.clone())
}
@ -78,7 +67,7 @@ impl Command for BytesReverse {
}
}
fn reverse(val: &Value, _args: &Arguments, span: Span) -> Value {
fn reverse(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match val {
Value::Binary {
val,

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@ -96,31 +97,12 @@ fn fmt(
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::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 };
}
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
pub fn action(input: &Value, span: Span) -> Value {
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input {
Value::Int { val, .. } => fmt_it(*val, span),
Value::Filesize { val, .. } => fmt_it(*val, span),

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@ -100,7 +101,7 @@ fn into_binary(
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
match input {
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::Binary {
@ -120,28 +121,11 @@ fn into_binary(
}
.into_pipeline_data())
}
_ => 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 };
_ => {
let arg = CellPathOnlyArgs::from(cell_paths);
operate(action, arg, input, call.head, engine_state.ctrlc.clone())
}
}
ret
}
},
engine_state.ctrlc.clone(),
),
}
}
fn int_to_endian(n: i64) -> Vec<u8> {
@ -160,7 +144,7 @@ fn float_to_endian(n: f64) -> Vec<u8> {
}
}
pub fn action(input: &Value, span: Span) -> Value {
pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input {
Value::Binary { .. } => input.clone(),
Value::Int { val, .. } => Value::Binary {

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@ -108,28 +109,9 @@ fn into_bool(
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(),
)
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
@ -154,7 +136,7 @@ fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
}
}
fn action(input: &Value, span: Span) -> Value {
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input {
Value::Bool { .. } => input.clone(),
Value::Int { val, .. } => Value::Bool {

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
use crate::{generate_strftime_list, parse_date_from_string};
use chrono::{DateTime, FixedOffset, Local, TimeZone, Utc};
use nu_engine::CallExt;
@ -5,14 +6,20 @@ use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
SyntaxShape, Value,
};
struct Arguments {
timezone: Option<Spanned<String>>,
offset: Option<Spanned<i64>>,
format: Option<String>,
column_paths: Vec<CellPath>,
zone_options: Option<Spanned<Zone>>,
format_options: Option<DatetimeFormat>,
cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
// In case it may be confused with chrono::TimeZone
@ -95,7 +102,36 @@ impl Command for SubCommand {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input)
if call.has_flag("list") {
Ok(generate_strftime_list(call.head, true).into_pipeline_data())
} else {
let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
// if zone-offset is specified, then zone will be neglected
let timezone = call.get_flag::<Spanned<String>>(engine_state, stack, "timezone")?;
let zone_options =
match &call.get_flag::<Spanned<i64>>(engine_state, stack, "offset")? {
Some(zone_offset) => Some(Spanned {
item: Zone::new(zone_offset.item),
span: zone_offset.span,
}),
None => timezone.as_ref().map(|zone| Spanned {
item: Zone::from_string(zone.item.clone()),
span: zone.span,
}),
};
let format_options = call
.get_flag::<String>(engine_state, stack, "format")?
.as_ref()
.map(|fmt| DatetimeFormat(fmt.to_string()));
let args = Arguments {
format_options,
zone_options,
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
}
fn usage(&self) -> &str {
@ -162,72 +198,9 @@ impl Command for SubCommand {
#[derive(Clone)]
struct DatetimeFormat(String);
fn operate(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let options = Arguments {
timezone: call.get_flag(engine_state, stack, "timezone")?,
offset: call.get_flag(engine_state, stack, "offset")?,
format: call.get_flag(engine_state, stack, "format")?,
column_paths: call.rest(engine_state, stack, 0)?,
};
// if zone-offset is specified, then zone will be neglected
let zone_options = match &options.offset {
Some(zone_offset) => Some(Spanned {
item: Zone::new(zone_offset.item),
span: zone_offset.span,
}),
None => options.timezone.as_ref().map(|zone| Spanned {
item: Zone::from_string(zone.item.clone()),
span: zone.span,
}),
};
let list_flag = call.has_flag("list");
let format_options = options
.format
.as_ref()
.map(|fmt| DatetimeFormat(fmt.to_string()));
input.map(
move |v| {
if options.column_paths.is_empty() && !list_flag {
action(&v, &zone_options, &format_options, head)
} else if list_flag {
generate_strftime_list(head, true)
} else {
let mut ret = v;
for path in &options.column_paths {
let zone_options = zone_options.clone();
let format_options = format_options.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &zone_options, &format_options, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action(
input: &Value,
timezone: &Option<Spanned<Zone>>,
dateformat: &Option<DatetimeFormat>,
head: Span,
) -> Value {
fn action(input: &Value, args: &Arguments, head: Span) -> Value {
let timezone = &args.zone_options;
let dateformat = &args.format_options;
// Check to see if input looks like a Unix timestamp (i.e. can it be parsed to an int?)
let timestamp = match input {
Value::Int { val, .. } => Ok(*val),
@ -359,7 +332,12 @@ mod tests {
fn takes_a_date_format() {
let date_str = Value::test_string("16.11.1984 8:00 am +0000");
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
let actual = action(&date_str, &None, &fmt_options, Span::test_data());
let args = Arguments {
zone_options: None,
format_options: fmt_options,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date {
val: DateTime::parse_from_str("16.11.1984 8:00 am +0000", "%d.%m.%Y %H:%M %P %z")
.unwrap(),
@ -371,7 +349,12 @@ mod tests {
#[test]
fn takes_iso8601_date_format() {
let date_str = Value::test_string("2020-08-04T16:39:18+00:00");
let actual = action(&date_str, &None, &None, Span::test_data());
let args = Arguments {
zone_options: None,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date {
val: DateTime::parse_from_str("2020-08-04T16:39:18+00:00", "%Y-%m-%dT%H:%M:%S%z")
.unwrap(),
@ -387,7 +370,12 @@ mod tests {
item: Zone::East(8),
span: Span::test_data(),
});
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
let args = Arguments {
zone_options: timezone_option,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date {
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
.unwrap(),
@ -404,7 +392,12 @@ mod tests {
item: Zone::East(8),
span: Span::test_data(),
});
let actual = action(&date_int, &timezone_option, &None, Span::test_data());
let args = Arguments {
zone_options: timezone_option,
format_options: None,
cell_paths: None,
};
let actual = action(&date_int, &args, Span::test_data());
let expected = Value::Date {
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
.unwrap(),
@ -421,7 +414,12 @@ mod tests {
item: Zone::Local,
span: Span::test_data(),
});
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
let args = Arguments {
zone_options: timezone_option,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date {
val: Local.timestamp(1614434140, 0).into(),
span: Span::test_data(),
@ -433,8 +431,12 @@ mod tests {
#[test]
fn takes_timestamp_without_timezone() {
let date_str = Value::test_string("1614434140");
let timezone_option = None;
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
let args = Arguments {
zone_options: None,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date {
val: Utc.timestamp(1614434140, 0).into(),
@ -451,7 +453,12 @@ mod tests {
item: Zone::Utc,
span: Span::test_data(),
});
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
let args = Arguments {
zone_options: timezone_option,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
assert_eq!(actual.get_type(), Error);
}
@ -460,7 +467,12 @@ mod tests {
fn communicates_parsing_error_given_an_invalid_datetimelike_string() {
let date_str = Value::test_string("16.11.1984 8:00 am Oops0000");
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
let actual = action(&date_str, &None, &fmt_options, Span::test_data());
let args = Arguments {
zone_options: None,
format_options: fmt_options,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
assert_eq!(actual.get_type(), Error);
}

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@ -36,7 +37,9 @@ impl Command for SubCommand {
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
operate(engine_state, stack, call, input)
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
fn examples(&self) -> Vec<Example> {
@ -72,37 +75,7 @@ impl Command for SubCommand {
}
}
fn operate(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::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 {
fn action(input: &Value, _args: &CellPathOnlyArgs, head: Span) -> Value {
match input {
Value::String { val: s, span } => {
let other = s.trim();
@ -163,7 +136,7 @@ mod tests {
let word = Value::test_string("3.1415");
let expected = Value::test_float(3.1415);
let actual = action(&word, Span::test_data());
let actual = action(&word, &CellPathOnlyArgs::from(vec![]), Span::test_data());
assert_eq!(actual, expected);
}
@ -171,7 +144,11 @@ mod tests {
fn communicates_parsing_error_given_an_invalid_decimallike_string() {
let decimal_str = Value::test_string("11.6anra");
let actual = action(&decimal_str, Span::test_data());
let actual = action(
&decimal_str,
&CellPathOnlyArgs::from(vec![]),
Span::test_data(),
);
assert_eq!(actual.get_type(), Error);
}
@ -180,7 +157,11 @@ mod tests {
fn int_to_decimal() {
let decimal_str = Value::test_int(10);
let expected = Value::test_float(10.0);
let actual = action(&decimal_str, Span::test_data());
let actual = action(
&decimal_str,
&CellPathOnlyArgs::from(vec![]),
Span::test_data(),
);
assert_eq!(actual, expected);
}

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@ -38,7 +39,9 @@ impl Command for SubCommand {
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
into_filesize(engine_state, stack, call, input)
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
fn examples(&self) -> Vec<Example> {
@ -84,37 +87,7 @@ impl Command for SubCommand {
}
}
fn into_filesize(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::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(),
)
}
pub fn action(input: &Value, span: Span) -> Value {
pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
if let Ok(value_span) = input.span() {
match input {
Value::Filesize { .. } => input.clone(),

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@ -6,11 +7,17 @@ use nu_protocol::{
};
struct Arguments {
radix: Option<Value>,
column_paths: Vec<CellPath>,
radix: u32,
cell_paths: Option<Vec<CellPath>>,
little_endian: bool,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
#[derive(Clone)]
pub struct SubCommand;
@ -46,7 +53,29 @@ impl Command for SubCommand {
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
into_int(engine_state, stack, call, input)
let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let radix = call.get_flag::<Value>(engine_state, stack, "radix")?;
let radix: u32 = match radix {
Some(Value::Int { val, span }) => {
if !(2..=36).contains(&val) {
return Err(ShellError::UnsupportedInput(
"Radix must lie in the range [2, 36]".to_string(),
span,
));
}
val as u32
}
Some(_) => 10,
None => 10,
};
let args = Arguments {
radix,
little_endian: call.has_flag("little-endian"),
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
fn examples(&self) -> Vec<Example> {
@ -121,59 +150,9 @@ impl Command for SubCommand {
}
}
fn into_int(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let options = Arguments {
radix: call.get_flag(engine_state, stack, "radix")?,
little_endian: call.has_flag("little-endian"),
column_paths: call.rest(engine_state, stack, 0)?,
};
let radix: u32 = match options.radix {
Some(Value::Int { val, .. }) => val as u32,
Some(_) => 10,
None => 10,
};
if let Some(val) = &options.radix {
if !(2..=36).contains(&radix) {
return Err(ShellError::UnsupportedInput(
"Radix must lie in the range [2, 36]".to_string(),
val.span()?,
));
}
}
input.map(
move |v| {
if options.column_paths.is_empty() {
action(&v, head, radix, options.little_endian)
} 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, radix, options.little_endian)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
pub fn action(input: &Value, span: Span, radix: u32, little_endian: bool) -> Value {
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
let radix = args.radix;
let little_endian = args.little_endian;
match input {
Value::Int { val: _, .. } => {
if radix == 10 {
@ -401,21 +380,45 @@ mod test {
let word = Value::test_string("10");
let expected = Value::test_int(10);
let actual = action(&word, Span::test_data(), 10, false);
let actual = action(
&word,
&Arguments {
radix: 10,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
#[test]
fn turns_binary_to_integer() {
let s = Value::test_string("0b101");
let actual = action(&s, Span::test_data(), 10, false);
let actual = action(
&s,
&Arguments {
radix: 10,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual, Value::test_int(5));
}
#[test]
fn turns_hex_to_integer() {
let s = Value::test_string("0xFF");
let actual = action(&s, Span::test_data(), 16, false);
let actual = action(
&s,
&Arguments {
radix: 16,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual, Value::test_int(255));
}
@ -423,7 +426,15 @@ mod test {
fn communicates_parsing_error_given_an_invalid_integerlike_string() {
let integer_str = Value::test_string("36anra");
let actual = action(&integer_str, Span::test_data(), 10, false);
let actual = action(
&integer_str,
&Arguments {
radix: 10,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual.get_type(), Error)
}

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@ -8,6 +9,19 @@ use nu_protocol::{
use nu_utils::get_system_locale;
use num_format::ToFormattedString;
struct Arguments {
decimals_value: Option<i64>,
decimals: bool,
cell_paths: Option<Vec<CellPath>>,
config: Config,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
#[derive(Clone)]
pub struct SubCommand;
@ -149,9 +163,6 @@ fn string_helper(
let decimals = call.has_flag("decimals");
let head = call.head;
let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let config = engine_state.get_config().clone();
if let Some(decimal_val) = decimals_value {
if decimals && decimal_val.is_negative() {
return Err(ShellError::UnsupportedInput(
@ -160,6 +171,15 @@ fn string_helper(
));
}
}
let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let config = engine_state.get_config().clone();
let args = Arguments {
decimals_value,
decimals,
cell_paths,
config,
};
match input {
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::String {
@ -179,45 +199,18 @@ fn string_helper(
}
.into_pipeline_data())
}
_ => input.map(
move |v| {
if column_paths.is_empty() {
action(&v, head, decimals, decimals_value, false, &config)
} else {
let mut ret = v;
for path in &column_paths {
let config = config.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| {
action(old, head, decimals, decimals_value, false, &config)
}),
);
if let Err(error) = r {
return Value::Error { error };
_ => operate(action, args, input, head, engine_state.ctrlc.clone()),
}
}
ret
}
},
engine_state.ctrlc.clone(),
),
}
}
pub fn action(
input: &Value,
span: Span,
decimals: bool,
digits: Option<i64>,
group_digits: bool,
config: &Config,
) -> Value {
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
let decimals = args.decimals;
let digits = args.decimals_value;
let config = &args.config;
match input {
Value::Int { val, .. } => {
let decimal_value = digits.unwrap_or(0) as usize;
let res = format_int(*val, group_digits, decimal_value);
let res = format_int(*val, false, decimal_value);
Value::String { val: res, span }
}
Value::Float { val, .. } => {

View File

@ -1,6 +1,8 @@
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::ast::{Call, CellPath};
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Span;
use nu_protocol::{Example, PipelineData, ShellError, Signature, SyntaxShape, Value};
use std::marker::PhantomData;
@ -26,6 +28,17 @@ impl<D: HashDigest> Default for GenericDigest<D> {
}
}
pub(super) struct Arguments {
pub(super) cell_paths: Option<Vec<CellPath>>,
pub(super) binary: bool,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
impl<D> Command for GenericDigest<D>
where
D: HashDigest + Send + Sync + 'static,
@ -66,31 +79,19 @@ where
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let binary = call.has_flag("binary");
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
input.map(
move |v| {
if cell_paths.is_empty() {
action::<D>(binary, &v)
} else {
let mut v = v;
for path in &cell_paths {
let ret = v.update_cell_path(
&path.members,
Box::new(move |old| action::<D>(binary, old)),
);
if let Err(error) = ret {
return Value::Error { error };
}
}
v
}
},
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
let args = Arguments { binary, cell_paths };
operate(
action::<D>,
args,
input,
call.head,
engine_state.ctrlc.clone(),
)
}
}
pub fn action<D>(binary: bool, input: &Value) -> Value
pub(super) fn action<D>(input: &Value, args: &Arguments, _span: Span) -> Value
where
D: HashDigest,
digest::Output<D>: core::fmt::LowerHex,
@ -119,7 +120,7 @@ where
let digest = D::digest(bytes);
if binary {
if args.binary {
Value::Binary {
val: digest.to_vec(),
span,

View File

@ -42,7 +42,7 @@ impl HashDigest for Md5 {
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::generic_digest;
use crate::hash::generic_digest::{self, Arguments};
#[test]
fn test_examples() {
@ -59,7 +59,14 @@ mod tests {
val: "c3fcd3d76192e4007dfb496cca67e13b".to_owned(),
span: Span::test_data(),
};
let actual = generic_digest::action::<Md5>(false, &binary);
let actual = generic_digest::action::<Md5>(
&binary,
&Arguments {
cell_paths: None,
binary: false,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
@ -73,7 +80,14 @@ mod tests {
val: "5f80e231382769b0102b1164cf722d83".to_owned(),
span: Span::test_data(),
};
let actual = generic_digest::action::<Md5>(false, &binary);
let actual = generic_digest::action::<Md5>(
&binary,
&Arguments {
cell_paths: None,
binary: false,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
}

View File

@ -44,7 +44,7 @@ impl HashDigest for Sha256 {
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::generic_digest;
use crate::hash::generic_digest::{self, Arguments};
#[test]
fn test_examples() {
@ -61,7 +61,14 @@ mod tests {
val: "71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73".to_owned(),
span: Span::test_data(),
};
let actual = generic_digest::action::<Sha256>(false, &binary);
let actual = generic_digest::action::<Sha256>(
&binary,
&Arguments {
cell_paths: None,
binary: false,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
@ -75,7 +82,14 @@ mod tests {
val: "c47a10dc272b1221f0380a2ae0f7d7fa830b3e378f2f5309bbf13f61ad211913".to_owned(),
span: Span::test_data(),
};
let actual = generic_digest::action::<Sha256>(false, &binary);
let actual = generic_digest::action::<Sha256>(
&binary,
&Arguments {
cell_paths: None,
binary: false,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
}

View File

@ -7,6 +7,28 @@ pub trait CmdArgument {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>>;
}
/// Arguments with only cell_path.
///
/// If commands is going to use `operate` function, and it only required optional cell_paths
/// Using this to simplify code.
pub struct CellPathOnlyArgs {
cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for CellPathOnlyArgs {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
impl From<Vec<CellPath>> for CellPathOnlyArgs {
fn from(cell_paths: Vec<CellPath>) -> Self {
Self {
cell_paths: (!cell_paths.is_empty()).then(|| cell_paths),
}
}
}
/// A simple wrapper for `PipelineData::map` method.
///
/// In detail, for each elements, invoking relative `cmd` with `arg`.

View File

@ -1,3 +1,4 @@
use crate::input_handler::{operate as general_operate, CmdArgument};
use base64::{decode_config, encode_config};
use nu_engine::CallExt;
use nu_protocol::ast::{Call, CellPath};
@ -20,6 +21,18 @@ pub enum ActionType {
Decode,
}
struct Arguments {
cell_paths: Option<Vec<CellPath>>,
binary: bool,
encoding_config: Base64Config,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
pub fn operate(
action_type: ActionType,
engine_state: &EngineState,
@ -31,7 +44,8 @@ pub fn operate(
let character_set: Option<Spanned<String>> =
call.get_flag(engine_state, stack, "character-set")?;
let binary = call.has_flag("binary");
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
// Default the character set to standard if the argument is not specified.
let character_set = match character_set {
@ -42,50 +56,27 @@ pub fn operate(
},
};
let encoding_config = Base64Config {
let args = Arguments {
encoding_config: Base64Config {
character_set,
action_type,
},
binary,
cell_paths,
};
input.map(
move |v| {
if column_paths.is_empty() {
match action(&v, binary, &encoding_config, &head) {
Ok(v) => v,
Err(e) => Value::Error { error: e },
}
} else {
let mut ret = v;
for path in &column_paths {
let config = encoding_config.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| match action(old, binary, &config, &head) {
Ok(v) => v,
Err(e) => Value::Error { error: e },
}),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
general_operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
fn action(
input: &Value,
// only used for `decode` action
output_binary: bool,
base64_config: &Base64Config,
command_span: &Span,
) -> Result<Value, ShellError> {
args: &Arguments,
command_span: Span,
) -> Value {
let base64_config = &args.encoding_config;
let output_binary = args.binary;
let config_character_set = &base64_config.character_set;
let base64_config_enum: base64::Config = match config_character_set.item.as_str() {
"standard" => base64::STANDARD,
@ -95,7 +86,7 @@ fn action(
"binhex" => base64::BINHEX,
"bcrypt" => base64::BCRYPT,
"crypt" => base64::CRYPT,
not_valid => return Err(ShellError::GenericError(
not_valid => return Value::Error { error:ShellError::GenericError(
"value is not an accepted character set".to_string(),
format!(
"{} is not a valid character-set.\nPlease use `help hash base64` to see a list of valid character sets.",
@ -104,28 +95,28 @@ fn action(
Some(config_character_set.span),
None,
Vec::new(),
))
)}
};
match input {
Value::Binary { val, .. } => match base64_config.action_type {
ActionType::Encode => Ok(Value::string(
encode_config(&val, base64_config_enum),
*command_span,
)),
ActionType::Decode => Err(ShellError::UnsupportedInput(
ActionType::Encode => {
Value::string(encode_config(&val, base64_config_enum), command_span)
}
ActionType::Decode => Value::Error {
error: ShellError::UnsupportedInput(
"Binary data can only support encoding".to_string(),
*command_span,
)),
command_span,
),
},
},
Value::String {
val,
span: value_span,
} => {
match base64_config.action_type {
ActionType::Encode => Ok(Value::string(
encode_config(&val, base64_config_enum),
*command_span,
)),
ActionType::Encode => {
Value::string(encode_config(&val, base64_config_enum), command_span)
}
ActionType::Decode => {
// for decode, input val may contains invalid new line character, which is ok to omitted them by default.
@ -135,46 +126,51 @@ fn action(
match decode_config(&val, base64_config_enum) {
Ok(decoded_value) => {
if output_binary {
Ok(Value::binary(decoded_value, *command_span))
Value::binary(decoded_value, command_span)
} else {
match String::from_utf8(decoded_value) {
Ok(string_value) => {
Ok(Value::string(string_value, *command_span))
}
Err(e) => Err(ShellError::GenericError(
"base64 payload isn't a valid utf-8 sequence".to_owned(),
Ok(string_value) => Value::string(string_value, command_span),
Err(e) => Value::Error {
error: ShellError::GenericError(
"base64 payload isn't a valid utf-8 sequence"
.to_owned(),
e.to_string(),
Some(*value_span),
Some("consider using the `--binary` flag".to_owned()),
Vec::new(),
)),
),
},
}
}
}
Err(_) => Err(ShellError::GenericError(
Err(_) => Value::Error {
error: ShellError::GenericError(
"value could not be base64 decoded".to_string(),
format!(
"invalid base64 input for character set {}",
&config_character_set.item
),
Some(*command_span),
Some(command_span),
None,
Vec::new(),
)),
),
},
}
}
}
}
other => Err(ShellError::TypeMismatch(
other => Value::Error {
error: ShellError::TypeMismatch(
format!("value is {}, not string", other.get_type()),
other.span()?,
)),
other.span().unwrap_or(command_span),
),
},
}
}
#[cfg(test)]
mod tests {
use super::{action, ActionType, Base64Config};
use super::{action, ActionType, Arguments, Base64Config};
use nu_protocol::{Span, Spanned, Value};
#[test]
@ -184,17 +180,19 @@ mod tests {
let actual = action(
&word,
true,
&Base64Config {
&Arguments {
encoding_config: Base64Config {
character_set: Spanned {
item: "standard".to_string(),
span: Span::test_data(),
},
action_type: ActionType::Encode,
},
&Span::test_data(),
)
.unwrap();
binary: true,
cell_paths: None,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
@ -205,17 +203,19 @@ mod tests {
let actual = action(
&word,
true,
&Base64Config {
&Arguments {
encoding_config: Base64Config {
character_set: Spanned {
item: "standard-no-padding".to_string(),
span: Span::test_data(),
},
action_type: ActionType::Encode,
},
&Span::test_data(),
)
.unwrap();
binary: true,
cell_paths: None,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
@ -226,17 +226,19 @@ mod tests {
let actual = action(
&word,
true,
&Base64Config {
&Arguments {
encoding_config: Base64Config {
character_set: Spanned {
item: "url-safe".to_string(),
span: Span::test_data(),
},
action_type: ActionType::Encode,
},
&Span::test_data(),
)
.unwrap();
binary: true,
cell_paths: None,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
@ -247,17 +249,19 @@ mod tests {
let actual = action(
&word,
true,
&Base64Config {
&Arguments {
encoding_config: Base64Config {
character_set: Spanned {
item: "binhex".to_string(),
span: Span::test_data(),
},
action_type: ActionType::Decode,
},
&Span::test_data(),
)
.unwrap();
binary: true,
cell_paths: None,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
@ -268,17 +272,19 @@ mod tests {
let actual = action(
&word,
true,
&Base64Config {
&Arguments {
encoding_config: Base64Config {
character_set: Spanned {
item: "binhex".to_string(),
span: Span::test_data(),
},
action_type: ActionType::Decode,
},
&Span::test_data(),
)
.unwrap();
binary: true,
cell_paths: None,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
@ -292,17 +298,19 @@ mod tests {
let actual = action(
&word,
true,
&Base64Config {
&Arguments {
encoding_config: Base64Config {
character_set: Spanned {
item: "standard".to_string(),
span: Span::test_data(),
},
action_type: ActionType::Encode,
},
&Span::test_data(),
)
.unwrap();
binary: true,
cell_paths: None,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
@ -315,16 +323,23 @@ mod tests {
let actual = action(
&word,
true,
&Base64Config {
&Arguments {
encoding_config: Base64Config {
character_set: Spanned {
item: "standard".to_string(),
span: Span::test_data(),
},
action_type: ActionType::Decode,
},
&Span::test_data(),
binary: true,
cell_paths: None,
},
Span::test_data(),
);
assert!(actual.is_err())
match actual {
Value::Error { .. } => {}
_ => panic!("the result should be Value::Error"),
}
}
}

View File

@ -1,4 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
@ -8,16 +8,6 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
#[derive(Clone)]
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 {
fn name(&self) -> &str {
"str length"
@ -49,8 +39,7 @@ impl Command for SubCommand {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
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 };
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
@ -73,7 +62,7 @@ impl Command for SubCommand {
}
}
fn action(input: &Value, _arg: &Arguments, head: Span) -> Value {
fn action(input: &Value, _arg: &CellPathOnlyArgs, head: Span) -> Value {
match input {
Value::String { val, .. } => Value::Int {
val: val.len() as i64,

View File

@ -1,4 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
@ -9,16 +9,6 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
#[derive(Clone)]
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 {
fn name(&self) -> &str {
"str reverse"
@ -50,8 +40,7 @@ impl Command for SubCommand {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
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 };
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
@ -90,7 +79,7 @@ impl Command for SubCommand {
}
}
fn action(input: &Value, _arg: &Arguments, head: Span) -> Value {
fn action(input: &Value, _arg: &CellPathOnlyArgs, head: Span) -> Value {
match input {
Value::String { val, .. } => Value::String {
val: val.chars().rev().collect::<String>(),