diff --git a/src/object/base.rs b/src/object/base.rs index 56ec87ad49..9075fc123b 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -347,6 +347,62 @@ impl Value { } } + pub fn get_data_by_path(&'a self, span: Span, path: &str) -> Option> { + let mut current = self; + for p in path.split(".") { + match current.get_data_by_key(p) { + Some(v) => current = v, + None => return None, + } + } + + Some(Spanned { + item: current, + span, + }) + } + + pub fn replace_data_at_path( + &'a self, + span: Span, + path: &str, + replaced_value: Value, + ) -> Option> { + let mut new_obj = self.clone(); + + let split_path: Vec<_> = path.split(".").collect(); + + if let Value::Object(ref mut o) = new_obj { + let mut current = o; + for idx in 0..split_path.len() { + match current.entries.get_mut(split_path[idx]) { + Some(next) => { + if idx == (split_path.len() - 1) { + *next = Spanned { + item: replaced_value, + span, + }; + return Some(Spanned { + item: new_obj, + span, + }); + } else { + match next.item { + Value::Object(ref mut o) => { + current = o; + } + _ => return None, + } + } + } + _ => return None, + } + } + } + + None + } + pub fn get_data(&'a self, desc: &String) -> MaybeOwned<'a, Value> { match self { p @ Value::Primitive(_) => MaybeOwned::Borrowed(p), diff --git a/src/parser/parse/span.rs b/src/parser/parse/span.rs index 30d4efeb79..d82f43468f 100644 --- a/src/parser/parse/span.rs +++ b/src/parser/parse/span.rs @@ -50,7 +50,7 @@ impl Spanned { } } - crate fn map(self, input: impl FnOnce(T) -> U) -> Spanned { + pub fn map(self, input: impl FnOnce(T) -> U) -> Spanned { let Spanned { span, item } = self; let mapped = input(item); diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index faf5ccc2b5..5c8d89129e 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -5,11 +5,56 @@ use nu::{ }; struct Inc { - inc_by: i64, + field: Option, } impl Inc { fn new() -> Inc { - Inc { inc_by: 1 } + Inc { field: None } + } + + fn inc(value: Spanned, field: &Option) -> Result, ShellError> { + match value.item { + Value::Primitive(Primitive::Int(i)) => Ok(Value::int(i + 1).spanned(value.span)), + Value::Primitive(Primitive::Bytes(b)) => { + Ok(Value::bytes(b + 1 as u64).spanned(value.span)) + } + Value::Primitive(Primitive::String(s)) => { + if let Ok(i) = s.parse::() { + Ok(Spanned { + item: Value::string(format!("{}", i + 1)), + span: value.span, + }) + } else { + Err(ShellError::string("string could not be incremented")) + } + } + Value::Object(_) => match field { + Some(f) => { + let replacement = match value.item.get_data_by_path(value.span, f) { + Some(result) => Inc::inc(result.map(|x| x.clone()), &None)?, + None => { + return Err(ShellError::string("inc could not find field to replace")) + } + }; + match value + .item + .replace_data_at_path(value.span, f, replacement.item.clone()) + { + Some(v) => return Ok(v), + None => { + return Err(ShellError::string("inc could not find field to replace")) + } + } + } + None => Err(ShellError::string( + "inc needs a field when incrementing a value in an object", + )), + }, + x => Err(ShellError::string(format!( + "Unrecognized type in stream: {:?}", + x + ))), + } } } @@ -17,7 +62,7 @@ impl Plugin for Inc { fn config(&mut self) -> Result { Ok(CommandConfig { name: "inc".to_string(), - positional: vec![PositionalType::optional_any("Increment")], + positional: vec![PositionalType::optional_any("Field")], is_filter: true, is_sink: false, named: IndexMap::new(), @@ -29,12 +74,17 @@ impl Plugin for Inc { for arg in args { match arg { Spanned { - item: Value::Primitive(Primitive::Int(i)), + item: Value::Primitive(Primitive::String(s)), .. } => { - self.inc_by = i; + self.field = Some(s); + } + _ => { + return Err(ShellError::string(format!( + "Unrecognized type in params: {:?}", + arg + ))) } - _ => return Err(ShellError::string("Unrecognized type in params")), } } } @@ -43,20 +93,7 @@ impl Plugin for Inc { } fn filter(&mut self, input: Spanned) -> Result, ShellError> { - let span = input.span; - - match input.item { - Value::Primitive(Primitive::Int(i)) => Ok(vec![ReturnSuccess::value( - Value::int(i + self.inc_by).spanned(span), - )]), - Value::Primitive(Primitive::Bytes(b)) => Ok(vec![ReturnSuccess::value( - Value::bytes(b + self.inc_by as u64).spanned(span), - )]), - x => Err(ShellError::string(format!( - "Unrecognized type in stream: {:?}", - x - ))), - } + Ok(vec![ReturnSuccess::value(Inc::inc(input, &self.field)?)]) } }