move out call info deserializing from str (#3294)

This commit is contained in:
Andrés N. Robalino
2021-04-09 22:58:18 -05:00
committed by GitHub
parent b19a7aa8a6
commit a131eddf54
28 changed files with 691 additions and 399 deletions

View File

@ -8,9 +8,8 @@ use nu_protocol::{
use nu_source::Tag;
use nu_value_ext::ValueExt;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
@ -45,18 +44,20 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest }, input) = args.process()?;
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arguments {
column_paths: params.rest_args()?,
})
})?;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
for path in &options.column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),

View File

@ -16,26 +16,28 @@ pub use pascal_case::SubCommand as PascalCase;
pub use screaming_snake_case::SubCommand as ScreamingSnakeCase;
pub use snake_case::SubCommand as SnakeCase;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
pub fn operate<F>(args: CommandArgs, case_operation: &'static F) -> Result<OutputStream, ShellError>
where
F: Fn(&str) -> String + Send + Sync + 'static,
{
let (Arguments { rest }, input) = args.process()?;
let (options, input) = args.extract(|params| {
Ok(Arguments {
column_paths: params.rest_args()?,
})
})?;
let column_paths: Vec<_> = rest;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag(), &case_operation)?)
} else {
let mut ret = v;
for path in &column_paths {
for path in &options.column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag(), &case_operation)),

View File

@ -6,6 +6,16 @@ use nu_source::Tagged;
pub struct SubCommand;
struct Arguments {
separator: Option<Tagged<String>>,
}
impl Arguments {
pub fn separator(&self) -> &str {
self.separator.as_ref().map_or("", |sep| &sep.item)
}
}
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"str collect"
@ -38,15 +48,18 @@ impl WholeStreamCommand for SubCommand {
pub fn collect(args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
//let (SubCommandArgs { separator }, input) = args.process()?;
let args = args.evaluate_once()?;
let separator: Option<Result<Tagged<String>, ShellError>> = args.opt(0);
let input = args.input;
let separator = if let Some(separator) = separator {
separator?.item
} else {
"".into()
};
let (options, input) = args.extract(|params| {
Ok(Arguments {
separator: if let Some(arg) = params.opt(0) {
Some(arg?)
} else {
None
},
})
})?;
let separator = options.separator();
let strings: Vec<Result<String, ShellError>> = input.map(|value| value.as_string()).collect();
let strings: Result<Vec<_>, _> = strings.into_iter().collect::<Result<_, _>>();

View File

@ -8,11 +8,10 @@ use nu_protocol::{
use nu_source::{Tag, Tagged};
use nu_value_ext::ValueExt;
#[derive(Deserialize)]
struct Arguments {
pattern: Tagged<String>,
rest: Vec<ColumnPath>,
insensitive: bool,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
@ -57,28 +56,27 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (
Arguments {
pattern,
rest,
insensitive,
},
input,
) = args.process()?;
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arc::new(Arguments {
pattern: params.req(0)?,
insensitive: params.has_flag("insensitive"),
column_paths: params.rest(1)?,
}))
})?;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, &pattern, insensitive, v.tag())?)
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, &options, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
let pattern = pattern.clone();
for path in &options.column_paths {
let options = options.clone();
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, &pattern, insensitive, old.tag())),
Box::new(move |old| action(old, &options, old.tag())),
)?;
}
@ -90,16 +88,19 @@ fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn action(
input: &Value,
pattern: &str,
insensitive: bool,
Arguments {
ref pattern,
insensitive,
..
}: &Arguments,
tag: impl Into<Tag>,
) -> Result<Value, ShellError> {
match &input.value {
UntaggedValue::Primitive(Primitive::String(s)) => {
let contains = if insensitive {
let contains = if *insensitive {
s.to_lowercase().contains(&pattern.to_lowercase())
} else {
s.contains(pattern)
s.contains(&pattern.item)
};
Ok(UntaggedValue::boolean(contains).into_value(tag))
@ -118,9 +119,9 @@ fn action(
#[cfg(test)]
mod tests {
use super::ShellError;
use super::{action, SubCommand};
use super::{action, Arguments, SubCommand};
use nu_protocol::UntaggedValue;
use nu_source::Tag;
use nu_source::{Tag, TaggedItem};
use nu_test_support::value::string;
#[test]
@ -133,44 +134,64 @@ mod tests {
#[test]
fn string_contains_other_string_case_sensitive() {
let word = string("Cargo.tomL");
let pattern = ".tomL";
let insensitive = false;
let expected = UntaggedValue::boolean(true).into_untagged_value();
let actual = action(&word, &pattern, insensitive, Tag::unknown()).unwrap();
let options = Arguments {
pattern: String::from(".tomL").tagged_unknown(),
insensitive: false,
column_paths: vec![],
};
let expected = UntaggedValue::boolean(true).into_untagged_value();
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn string_does_not_contain_other_string_case_sensitive() {
let word = string("Cargo.tomL");
let pattern = "Lomt.";
let insensitive = false;
let expected = UntaggedValue::boolean(false).into_untagged_value();
let actual = action(&word, &pattern, insensitive, Tag::unknown()).unwrap();
let options = Arguments {
pattern: String::from("Lomt.").tagged_unknown(),
insensitive: false,
column_paths: vec![],
};
let expected = UntaggedValue::boolean(false).into_untagged_value();
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn string_contains_other_string_case_insensitive() {
let word = string("Cargo.ToMl");
let pattern = ".TOML";
let insensitive = true;
let expected = UntaggedValue::boolean(true).into_untagged_value();
let actual = action(&word, &pattern, insensitive, Tag::unknown()).unwrap();
let options = Arguments {
pattern: String::from(".TOML").tagged_unknown(),
insensitive: true,
column_paths: vec![],
};
let expected = UntaggedValue::boolean(true).into_untagged_value();
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn string_does_not_contain_other_string_case_insensitive() {
let word = string("Cargo.tOml");
let pattern = "lomt.";
let insensitive = true;
let expected = UntaggedValue::boolean(false).into_untagged_value();
let actual = action(&word, &pattern, insensitive, Tag::unknown()).unwrap();
let options = Arguments {
pattern: String::from("lomt.").tagged_unknown(),
insensitive: true,
column_paths: vec![],
};
let expected = UntaggedValue::boolean(false).into_untagged_value();
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
}
}

View File

@ -8,9 +8,8 @@ use nu_protocol::{
use nu_source::Tag;
use nu_value_ext::ValueExt;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
@ -45,18 +44,20 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest }, input) = args.process()?;
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arguments {
column_paths: params.rest_args()?,
})
})?;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
for path in &options.column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
@ -102,9 +103,8 @@ mod tests {
#[test]
fn downcases() {
let word = string("ANDRES");
let expected = string("andres");
let actual = action(&word, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
assert_eq!(actual, string("andres"));
}
}

View File

@ -8,12 +8,12 @@ use nu_protocol::{
use nu_source::{Tag, Tagged};
use nu_value_ext::ValueExt;
#[derive(Deserialize)]
pub struct SubCommand;
struct Arguments {
pattern: Tagged<String>,
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
@ -47,22 +47,26 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { pattern, rest }, input) = args.process()?;
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arc::new(Arguments {
pattern: params.req(0)?,
column_paths: params.rest(1)?,
}))
})?;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, &pattern, v.tag())?)
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, &options.pattern, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
let pattern = pattern.clone();
for path in &options.column_paths {
let options = options.clone();
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, &pattern, old.tag())),
Box::new(move |old| action(old, &options.pattern, old.tag())),
)?;
}

View File

@ -9,12 +9,11 @@ use nu_source::{Tag, Tagged};
use nu_value_ext::ValueExt;
use regex::Regex;
#[derive(Deserialize)]
struct Arguments {
all: bool,
find: Tagged<String>,
replace: Tagged<String>,
rest: Vec<ColumnPath>,
all: bool,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
@ -59,35 +58,31 @@ impl WholeStreamCommand for SubCommand {
}
}
#[derive(Clone)]
struct FindReplace(String, String);
struct FindReplace<'a>(&'a str, &'a str);
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (
Arguments {
find,
replace,
rest,
all,
},
input,
) = args.process()?;
let options = FindReplace(find.item, replace.item);
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arc::new(Arguments {
all: params.has_flag("all"),
find: params.req(0)?,
replace: params.req(1)?,
column_paths: params.rest(2)?,
}))
})?;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, &options, v.tag(), all)?)
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, &options, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
for path in &options.column_paths {
let options = options.clone();
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, &options, old.tag(), all)),
Box::new(move |old| action(old, &options, old.tag())),
)?;
}
@ -99,29 +94,27 @@ fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn action(
input: &Value,
options: &FindReplace,
Arguments {
find, replace, all, ..
}: &Arguments,
tag: impl Into<Tag>,
all: bool,
) -> Result<Value, ShellError> {
match &input.value {
UntaggedValue::Primitive(Primitive::String(s)) => {
let find = &options.0;
let replacement = &options.1;
let FindReplace(find, replacement) = FindReplace(&find, &replace);
let regex = Regex::new(find);
let regex = Regex::new(find.as_str());
let out = match regex {
Ok(match regex {
Ok(re) => {
if all {
UntaggedValue::string(re.replace_all(s, replacement.as_str()).to_owned())
if *all {
UntaggedValue::string(re.replace_all(s, replacement).to_owned())
} else {
UntaggedValue::string(re.replace(s, replacement.as_str()).to_owned())
UntaggedValue::string(re.replace(s, replacement).to_owned())
}
}
Err(_) => UntaggedValue::string(s),
};
Ok(out.into_value(tag))
}
.into_value(tag))
}
other => {
let got = format!("got {}", other.type_name());
@ -137,8 +130,8 @@ fn action(
#[cfg(test)]
mod tests {
use super::ShellError;
use super::{action, FindReplace, SubCommand};
use nu_source::Tag;
use super::{action, Arguments, SubCommand};
use nu_source::{Tag, TaggedItem};
use nu_test_support::value::string;
#[test]
@ -151,11 +144,15 @@ mod tests {
#[test]
fn can_have_capture_groups() {
let word = string("Cargo.toml");
let expected = string("Carga.toml");
let all = false;
let find_replace_options = FindReplace("Cargo.(.+)".to_string(), "Carga.$1".to_string());
let actual = action(&word, &find_replace_options, Tag::unknown(), all).unwrap();
assert_eq!(actual, expected);
let options = Arguments {
find: String::from("Cargo.(.+)").tagged_unknown(),
replace: String::from("Carga.$1").tagged_unknown(),
column_paths: vec![],
all: false,
};
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, string("Carga.toml"));
}
}

View File

@ -14,12 +14,10 @@ use std::iter;
pub struct SubCommand;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
decimals: Option<Tagged<u64>>,
#[serde(rename(deserialize = "group-digits"))]
group_digits: bool,
column_paths: Vec<ColumnPath>,
}
impl WholeStreamCommand for SubCommand {
@ -39,15 +37,6 @@ impl WholeStreamCommand for SubCommand {
"decimal digits to which to round",
Some('d'),
)
/*
FIXME: this isn't currently supported because of num_format being out of date. Once it's updated, re-enable this
.switch(
"group-digits",
// TODO according to system localization
"group digits, currently by thousand with commas",
Some('g'),
)
*/
}
fn usage(&self) -> &str {
@ -80,23 +69,28 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (
Arguments {
decimals,
group_digits,
rest: column_paths,
},
input,
) = args.process()?;
let digits = decimals.map(|tagged| tagged.item);
let (options, input) = args.extract(|params| {
Ok(Arguments {
decimals: if let Some(arg) = params.get_flag("decimals") {
Some(arg?)
} else {
None
},
group_digits: false,
column_paths: params.rest_args()?,
})
})?;
let digits = options.decimals.as_ref().map(|tagged| tagged.item);
let group_digits = options.group_digits;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag(), digits, group_digits)?)
} else {
let mut ret = v;
for path in &column_paths {
for path in &options.column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag(), digits, group_digits)),

View File

@ -8,12 +8,11 @@ use nu_protocol::{
use nu_source::{Tag, Tagged};
use nu_value_ext::{as_string, ValueExt};
#[derive(Deserialize)]
struct Arguments {
pattern: Tagged<String>,
rest: Vec<ColumnPath>,
range: Option<Value>,
end: bool,
pattern: Tagged<String>,
range: Option<Value>,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
@ -91,33 +90,32 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (
Arguments {
pattern,
rest,
range,
end,
},
input,
) = args.process()?;
let range = range.unwrap_or_else(|| {
UntaggedValue::Primitive(Primitive::String("".to_string())).into_untagged_value()
});
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arc::new(Arguments {
pattern: params.req(0)?,
range: if let Some(arg) = params.get_flag("range") {
Some(arg?)
} else {
None
},
end: params.has_flag("end"),
column_paths: params.rest(1)?,
}))
})?;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, &pattern, &range, end, v.tag())?)
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, &options, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
let range = range.clone();
let pattern = pattern.clone();
for path in &options.column_paths {
let options = options.clone();
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, &pattern, &range, end, old.tag())),
Box::new(move |old| action(old, &options, old.tag())),
)?;
}
@ -129,29 +127,38 @@ fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn action(
input: &Value,
pattern: &str,
range: &Value,
end: bool,
Arguments {
ref pattern,
range,
end,
..
}: &Arguments,
tag: impl Into<Tag>,
) -> Result<Value, ShellError> {
let tag = tag.into();
let range = match range {
Some(range) => range.clone(),
None => UntaggedValue::string("").into_value(&tag),
};
let r = process_range(&input, &range)?;
match &input.value {
UntaggedValue::Primitive(Primitive::String(s)) => {
let start_index = r.0 as usize;
let end_index = r.1 as usize;
if end {
if let Some(result) = s[start_index..end_index].rfind(pattern) {
if *end {
if let Some(result) = s[start_index..end_index].rfind(&**pattern) {
Ok(UntaggedValue::int(result + start_index).into_value(tag))
} else {
let not_found = -1;
Ok(UntaggedValue::int(not_found).into_value(tag))
Ok(UntaggedValue::int(-1).into_value(tag))
}
} else if let Some(result) = s[start_index..end_index].find(pattern) {
} else if let Some(result) = s[start_index..end_index].find(&**pattern) {
Ok(UntaggedValue::int(result + start_index).into_value(tag))
} else {
let not_found = -1;
Ok(UntaggedValue::int(not_found).into_value(tag))
Ok(UntaggedValue::int(-1).into_value(tag))
}
}
other => {
@ -159,7 +166,7 @@ fn action(
Err(ShellError::labeled_error(
"value is not string",
got,
tag.into().span,
tag.span,
))
}
}
@ -234,10 +241,9 @@ fn process_range(input: &Value, range: &Value) -> Result<IndexOfOptionalBounds,
#[cfg(test)]
mod tests {
use super::ShellError;
use super::{action, SubCommand};
use nu_protocol::{Primitive, UntaggedValue};
use nu_source::Tag;
use nu_test_support::value::string;
use super::{action, Arguments, SubCommand};
use nu_source::{Tag, TaggedItem};
use nu_test_support::value::{int, string};
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
@ -249,76 +255,90 @@ mod tests {
#[test]
fn returns_index_of_substring() {
let word = string("Cargo.tomL");
let pattern = ".tomL";
let end = false;
let index_of_bounds =
UntaggedValue::Primitive(Primitive::String("".to_string())).into_untagged_value();
let expected = UntaggedValue::Primitive(Primitive::Int(5.into())).into_untagged_value();
let actual = action(&word, &pattern, &index_of_bounds, end, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
let options = Arguments {
pattern: String::from(".tomL").tagged_unknown(),
range: Some(string("")),
column_paths: vec![],
end: false,
};
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, int(5));
}
#[test]
fn index_of_does_not_exist_in_string() {
let word = string("Cargo.tomL");
let pattern = "Lm";
let end = false;
let index_of_bounds =
UntaggedValue::Primitive(Primitive::String("".to_string())).into_untagged_value();
let expected = UntaggedValue::Primitive(Primitive::Int((-1).into())).into_untagged_value();
let actual = action(&word, &pattern, &index_of_bounds, end, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
let options = Arguments {
pattern: String::from("Lm").tagged_unknown(),
range: Some(string("")),
column_paths: vec![],
end: false,
};
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, int(-1));
}
#[test]
fn returns_index_of_next_substring() {
let word = string("Cargo.Cargo");
let pattern = "Cargo";
let end = false;
let index_of_bounds =
UntaggedValue::Primitive(Primitive::String("1,".to_string())).into_untagged_value();
let expected = UntaggedValue::Primitive(Primitive::Int(6.into())).into_untagged_value();
let actual = action(&word, &pattern, &index_of_bounds, end, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
let options = Arguments {
pattern: String::from("Cargo").tagged_unknown(),
range: Some(string("1,")),
column_paths: vec![],
end: false,
};
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, int(6));
}
#[test]
fn index_does_not_exist_due_to_end_index() {
let word = string("Cargo.Banana");
let pattern = "Banana";
let end = false;
let index_of_bounds =
UntaggedValue::Primitive(Primitive::String(",5".to_string())).into_untagged_value();
let expected = UntaggedValue::Primitive(Primitive::Int((-1).into())).into_untagged_value();
let actual = action(&word, &pattern, &index_of_bounds, end, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
let options = Arguments {
pattern: String::from("Banana").tagged_unknown(),
range: Some(string(",5")),
column_paths: vec![],
end: false,
};
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, int(-1));
}
#[test]
fn returns_index_of_nums_in_middle_due_to_index_limit_from_both_ends() {
let word = string("123123123");
let pattern = "123";
let end = false;
let index_of_bounds =
UntaggedValue::Primitive(Primitive::String("2,6".to_string())).into_untagged_value();
let expected = UntaggedValue::Primitive(Primitive::Int(3.into())).into_untagged_value();
let actual = action(&word, &pattern, &index_of_bounds, end, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
let options = Arguments {
pattern: String::from("123").tagged_unknown(),
range: Some(string("2,6")),
column_paths: vec![],
end: false,
};
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, int(3));
}
#[test]
fn index_does_not_exists_due_to_strict_bounds() {
let word = string("123456");
let pattern = "1";
let end = false;
let index_of_bounds =
UntaggedValue::Primitive(Primitive::String("2,4".to_string())).into_untagged_value();
let expected = UntaggedValue::Primitive(Primitive::Int((-1).into())).into_untagged_value();
let actual = action(&word, &pattern, &index_of_bounds, end, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
let options = Arguments {
pattern: String::from("1").tagged_unknown(),
range: Some(string("2,4")),
column_paths: vec![],
end: false,
};
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, int(-1));
}
}

View File

@ -8,9 +8,8 @@ use nu_protocol::{
pub struct SubCommand;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
impl WholeStreamCommand for SubCommand {
@ -53,17 +52,20 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest }, input) = args.process()?;
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arguments {
column_paths: params.rest_args()?,
})
})?;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
for path in &options.column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),

View File

@ -8,11 +8,10 @@ use nu_protocol::{
use nu_source::{Tag, Tagged};
use nu_value_ext::ValueExt;
#[derive(Deserialize)]
struct Arguments {
length: Tagged<usize>,
character: Tagged<String>,
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
@ -78,30 +77,27 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (
Arguments {
length,
character,
rest,
},
input,
) = args.process()?;
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arc::new(Arguments {
length: params.req_named("length")?,
character: params.req_named("character")?,
column_paths: params.rest_args()?,
}))
})?;
Ok(input
.map(move |v| {
let len = length.item;
let character = character.item.clone();
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, len, character, v.tag())?)
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, &options, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
let str_clone = character.clone();
for path in &options.column_paths {
let options = options.clone();
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, len, str_clone, old.tag())),
Box::new(move |old| action(old, &options, old.tag())),
)?;
}
@ -113,19 +109,20 @@ fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn action(
input: &Value,
length: usize,
character: String,
Arguments {
character, length, ..
}: &Arguments,
tag: impl Into<Tag>,
) -> Result<Value, ShellError> {
match &input.value {
UntaggedValue::Primitive(Primitive::String(s)) => {
if length < s.len() {
if **length < s.len() {
Ok(
UntaggedValue::string(s.chars().take(length).collect::<String>())
UntaggedValue::string(s.chars().take(**length).collect::<String>())
.into_value(tag),
)
} else {
let mut res = character.repeat(length - s.chars().count());
let mut res = character.repeat(**length - s.chars().count());
res += s.as_ref();
Ok(UntaggedValue::string(res).into_value(tag))
}
@ -143,10 +140,10 @@ fn action(
#[cfg(test)]
mod tests {
use super::{action, SubCommand};
use super::{action, Arguments, SubCommand};
use nu_errors::ShellError;
use nu_protocol::UntaggedValue;
use nu_source::Tag;
use nu_source::{Tag, TaggedItem};
use nu_test_support::value::string;
#[test]
@ -159,22 +156,32 @@ mod tests {
#[test]
fn left_pad_with_zeros() {
let word = string("123");
let pad_char = '0'.to_string();
let pad_len = 10;
let expected = UntaggedValue::string("0000000123").into_untagged_value();
let actual = action(&word, pad_len, pad_char, Tag::unknown()).unwrap();
let options = Arguments {
character: String::from("0").tagged_unknown(),
length: 10_usize.tagged_unknown(),
column_paths: vec![],
};
let expected = UntaggedValue::string("0000000123").into_untagged_value();
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn left_pad_but_truncate() {
let word = string("123456789");
let pad_char = '0'.to_string();
let pad_len = 3;
let expected = UntaggedValue::string("123").into_untagged_value();
let actual = action(&word, pad_len, pad_char, Tag::unknown()).unwrap();
let options = Arguments {
character: String::from("0").tagged_unknown(),
length: 3_usize.tagged_unknown(),
column_paths: vec![],
};
let expected = UntaggedValue::string("123").into_untagged_value();
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
}
}

View File

@ -8,9 +8,8 @@ use nu_protocol::{
pub struct SubCommand;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
impl WholeStreamCommand for SubCommand {
@ -43,17 +42,20 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest }, input) = args.process()?;
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arguments {
column_paths: params.rest_args()?,
})
})?;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
for path in &options.column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),

View File

@ -8,11 +8,10 @@ use nu_protocol::{
use nu_source::{Tag, Tagged};
use nu_value_ext::ValueExt;
#[derive(Deserialize)]
struct Arguments {
length: Tagged<usize>,
character: Tagged<String>,
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
@ -78,30 +77,27 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (
Arguments {
length,
character,
rest,
},
input,
) = args.process()?;
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arc::new(Arguments {
length: params.req_named("length")?,
character: params.req_named("character")?,
column_paths: params.rest_args()?,
}))
})?;
Ok(input
.map(move |v| {
let len = length.item;
let character = character.item.clone();
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, len, character, v.tag())?)
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, &options, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
let str_clone = character.clone();
for path in &options.column_paths {
let options = options.clone();
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, len, str_clone, old.tag())),
Box::new(move |old| action(old, &options, old.tag())),
)?;
}
@ -113,20 +109,21 @@ fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
fn action(
input: &Value,
length: usize,
character: String,
Arguments {
length, character, ..
}: &Arguments,
tag: impl Into<Tag>,
) -> Result<Value, ShellError> {
match &input.value {
UntaggedValue::Primitive(Primitive::String(s)) => {
if length < s.len() {
if **length < s.len() {
Ok(
UntaggedValue::string(s.chars().take(length).collect::<String>())
UntaggedValue::string(s.chars().take(**length).collect::<String>())
.into_value(tag),
)
} else {
let mut res = s.to_string();
res += character.repeat(length - s.chars().count()).as_str();
res += character.repeat(**length - s.chars().count()).as_str();
Ok(UntaggedValue::string(res).into_value(tag))
}
}
@ -143,10 +140,10 @@ fn action(
#[cfg(test)]
mod tests {
use super::{action, SubCommand};
use super::{action, Arguments, SubCommand};
use nu_errors::ShellError;
use nu_protocol::UntaggedValue;
use nu_source::Tag;
use nu_source::{Tag, TaggedItem};
use nu_test_support::value::string;
#[test]
@ -159,22 +156,32 @@ mod tests {
#[test]
fn right_pad_with_zeros() {
let word = string("123");
let pad_char = '0'.to_string();
let pad_len = 10;
let expected = UntaggedValue::string("1230000000").into_untagged_value();
let actual = action(&word, pad_len, pad_char, Tag::unknown()).unwrap();
let options = Arguments {
character: String::from("0").tagged_unknown(),
length: 10_usize.tagged_unknown(),
column_paths: vec![],
};
let expected = UntaggedValue::string("1230000000").into_untagged_value();
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn right_pad_but_truncate() {
let word = string("123456789");
let pad_char = '0'.to_string();
let pad_len = 3;
let expected = UntaggedValue::string("123").into_untagged_value();
let actual = action(&word, pad_len, pad_char, Tag::unknown()).unwrap();
let options = Arguments {
character: String::from("0").tagged_unknown(),
length: 3_usize.tagged_unknown(),
column_paths: vec![],
};
let expected = UntaggedValue::string("123").into_untagged_value();
let actual = action(&word, &options, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
}
}

View File

@ -8,12 +8,12 @@ use nu_protocol::{
use nu_source::{Tag, Tagged};
use nu_value_ext::ValueExt;
#[derive(Deserialize)]
pub struct SubCommand;
struct Arguments {
pattern: Tagged<String>,
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
@ -47,22 +47,26 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { pattern, rest }, input) = args.process()?;
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arc::new(Arguments {
pattern: params.req(0)?,
column_paths: params.rest(1)?,
}))
})?;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, &pattern, v.tag())?)
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, &options.pattern, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
let pattern = pattern.clone();
for path in &options.column_paths {
let options = options.clone();
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, &pattern, old.tag())),
Box::new(move |old| action(old, &options.pattern, old.tag())),
)?;
}

View File

@ -11,10 +11,9 @@ use nu_value_ext::{as_string, ValueExt};
use std::cmp::Ordering;
use std::convert::TryInto;
#[derive(Deserialize)]
struct Arguments {
range: Value,
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
@ -90,24 +89,28 @@ struct SubstringText(String, String);
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let (Arguments { range, rest }, input) = args.process()?;
let (options, input) = args.extract(|params| {
Ok(Arguments {
range: params.req(0)?,
column_paths: params.rest(1)?,
})
})?;
let column_paths: Vec<_> = rest;
let options = process_arguments(range, name)?.into();
let indexes = Arc::new(process_arguments(&options, name)?.into());
Ok(input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, &options, v.tag())?)
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, &indexes, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
let options = options.clone();
for path in &options.column_paths {
let indexes = indexes.clone();
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, &options, old.tag())),
Box::new(move |old| action(old, &indexes, old.tag())),
)?;
}
@ -174,10 +177,13 @@ fn action(input: &Value, options: &Substring, tag: impl Into<Tag>) -> Result<Val
}
}
fn process_arguments(range: Value, name: impl Into<Tag>) -> Result<(isize, isize), ShellError> {
fn process_arguments(
options: &Arguments,
name: impl Into<Tag>,
) -> Result<(isize, isize), ShellError> {
let name = name.into();
let search = match &range.value {
let search = match &options.range.value {
UntaggedValue::Table(indexes) => {
if indexes.len() > 2 {
Err(ShellError::labeled_error(

View File

@ -1,4 +1,5 @@
use crate::prelude::*;
use crate::utils::arguments::arguments;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
@ -10,12 +11,11 @@ use nu_value_ext::ValueExt;
use chrono::{DateTime, FixedOffset, Local, LocalResult, Offset, TimeZone, Utc};
#[derive(Deserialize)]
struct Arguments {
timezone: Option<Tagged<String>>,
offset: Option<Tagged<i16>>,
format: Option<Tagged<String>>,
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
// In case it may be confused with chrono::TimeZone
@ -78,7 +78,7 @@ impl WholeStreamCommand for SubCommand {
Some('f'),
)
.rest(
SyntaxShape::ColumnPath,
SyntaxShape::Any,
"optionally convert text into datetime by column paths",
)
}
@ -127,55 +127,62 @@ impl WholeStreamCommand for SubCommand {
struct DatetimeFormat(String);
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (
Arguments {
timezone,
offset,
format,
rest,
},
input,
) = args.process()?;
let (options, input) = args.extract(|params| {
let (column_paths, _) = arguments(&mut params.rest_args()?)?;
let column_paths: Vec<_> = rest;
Ok(Arguments {
timezone: if let Some(arg) = params.get_flag("timezone") {
Some(arg?)
} else {
None
},
offset: if let Some(arg) = params.get_flag("offset") {
Some(arg?)
} else {
None
},
format: if let Some(arg) = params.get_flag("format") {
Some(arg?)
} else {
None
},
column_paths,
})
})?;
// if zone-offset is specified, then zone will be neglected
let zone_options = if let Some(Tagged {
item: zone_offset,
tag: _tag,
}) = offset
tag,
}) = &options.offset
{
Some(Tagged {
item: Zone::new(zone_offset),
tag: _tag,
item: Zone::new(*zone_offset),
tag: tag.into(),
})
} else if let Some(Tagged {
item: zone,
tag: _tag,
}) = timezone
{
} else if let Some(Tagged { item: zone, tag }) = &options.timezone {
Some(Tagged {
item: Zone::from_string(zone),
tag: _tag,
item: Zone::from_string(zone.clone()),
tag: tag.into(),
})
} else {
None
};
let format_options = if let Some(Tagged { item: fmt, .. }) = format {
Some(DatetimeFormat(fmt))
let format_options = if let Some(Tagged { item: fmt, .. }) = &options.format {
Some(DatetimeFormat(fmt.to_string()))
} else {
None
};
Ok(input
.map(move |v| {
if column_paths.is_empty() {
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, &zone_options, &format_options, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
for path in &options.column_paths {
let zone_options = zone_options.clone();
let format_options = format_options.clone();

View File

@ -11,9 +11,8 @@ use nu_value_ext::ValueExt;
use bigdecimal::BigDecimal;
use std::str::FromStr;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
@ -48,18 +47,20 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest }, input) = args.process()?;
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arguments {
column_paths: params.rest_args()?,
})
})?;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
for path in &options.column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),

View File

@ -3,17 +3,18 @@ use crate::utils::arguments::arguments;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::ShellTypeName;
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::{Tag, Tagged};
use nu_value_ext::ValueExt;
use num_bigint::BigInt;
use num_traits::Num;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<Value>,
radix: Option<Tagged<u32>>,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
@ -67,19 +68,29 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { mut rest, radix }, input) = args.process()?;
let (column_paths, _) = arguments(&mut rest)?;
let (options, input) = args.extract(|params| {
let (column_paths, _) = arguments(&mut params.rest_args()?)?;
let radix = radix.map(|r| r.item).unwrap_or(10);
Ok(Arguments {
radix: if let Some(arg) = params.get_flag("radix") {
Some(arg?)
} else {
None
},
column_paths,
})
})?;
let radix = options.radix.as_ref().map(|r| r.item).unwrap_or(10);
Ok(input
.map(move |v| {
if column_paths.is_empty() {
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag(), radix)?)
} else {
let mut ret = v;
for path in &column_paths {
for path in &options.column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag(), radix)),

View File

@ -14,25 +14,31 @@ pub use trim_both_ends::SubCommand as Trim;
pub use trim_left::SubCommand as TrimLeft;
pub use trim_right::SubCommand as TrimRight;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
#[serde(rename(deserialize = "char"))]
char_: Option<Tagged<char>>,
character: Option<Tagged<char>>,
column_paths: Vec<ColumnPath>,
}
pub fn operate<F>(args: CommandArgs, trim_operation: &'static F) -> Result<OutputStream, ShellError>
where
F: Fn(&str, Option<char>) -> String + Send + Sync + 'static,
{
let (Arguments { rest, char_ }, input) = args.process()?;
let (options, input) = args.extract(|params| {
Ok(Arc::new(Arguments {
character: if let Some(arg) = params.get_flag("char") {
Some(arg?)
} else {
None
},
column_paths: params.rest_args()?,
}))
})?;
let column_paths: Vec<_> = rest;
let to_trim = char_.map(|tagged| tagged.item);
let to_trim = options.character.as_ref().map(|tagged| tagged.item);
Ok(input
.map(move |v| {
if column_paths.is_empty() {
if options.column_paths.is_empty() {
ReturnSuccess::value(action(
&v,
v.tag(),
@ -43,7 +49,7 @@ where
} else {
let mut ret = v;
for path in &column_paths {
for path in &options.column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| {

View File

@ -8,9 +8,8 @@ use nu_protocol::{
use nu_source::Tag;
use nu_value_ext::ValueExt;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
column_paths: Vec<ColumnPath>,
}
pub struct SubCommand;
@ -45,18 +44,20 @@ impl WholeStreamCommand for SubCommand {
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { rest }, input) = args.process()?;
let column_paths: Vec<_> = rest;
let (options, input) = args.extract(|params| {
Ok(Arguments {
column_paths: params.rest_args()?,
})
})?;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
if options.column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
for path in &options.column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
@ -102,9 +103,8 @@ mod tests {
#[test]
fn upcases() {
let word = string("andres");
let expected = string("ANDRES");
let actual = action(&word, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
assert_eq!(actual, string("ANDRES"));
}
}