From 3b682046b77827a5a8499aa7de9438749c76eff0 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 18 Jul 2019 13:32:19 +1200 Subject: [PATCH 1/3] new inc takes a path --- src/object/base.rs | 56 +++++++++++++++++++++++++++++ src/parser/parse/span.rs | 2 +- src/plugins/inc.rs | 77 +++++++++++++++++++++++++++++----------- 3 files changed, 114 insertions(+), 21 deletions(-) diff --git a/src/object/base.rs b/src/object/base.rs index 56ec87ad4..9075fc123 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 30d4efeb7..d82f43468 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 faf5ccc2b..5c8d89129 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)?)]) } } From e66c687da63b8cbf3561421c1923380576a214d8 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 18 Jul 2019 13:49:12 +1200 Subject: [PATCH 2/3] Add semver increment --- src/plugins/inc.rs | 52 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index 5c8d89129..8d69f91d5 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -1,18 +1,30 @@ use indexmap::IndexMap; use nu::{ - serve_plugin, Args, CommandConfig, Plugin, PositionalType, Primitive, ReturnSuccess, + serve_plugin, Args, CommandConfig, NamedType, Plugin, PositionalType, Primitive, ReturnSuccess, ReturnValue, ShellError, Spanned, SpannedItem, Value, }; struct Inc { field: Option, + major: bool, + minor: bool, + patch: bool, } impl Inc { fn new() -> Inc { - Inc { field: None } + Inc { + field: None, + major: false, + minor: false, + patch: false, + } } - fn inc(value: Spanned, field: &Option) -> Result, ShellError> { + fn inc( + &self, + 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)) => { @@ -24,6 +36,19 @@ impl Inc { item: Value::string(format!("{}", i + 1)), span: value.span, }) + } else if let Ok(mut ver) = semver::Version::parse(&s) { + if self.major { + ver.increment_major(); + } else if self.minor { + ver.increment_minor(); + } else { + self.patch; + ver.increment_patch(); + } + Ok(Spanned { + item: Value::string(ver.to_string()), + span: value.span, + }) } else { Err(ShellError::string("string could not be incremented")) } @@ -31,7 +56,7 @@ impl Inc { 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)?, + Some(result) => self.inc(result.map(|x| x.clone()), &None)?, None => { return Err(ShellError::string("inc could not find field to replace")) } @@ -60,16 +85,31 @@ impl Inc { impl Plugin for Inc { fn config(&mut self) -> Result { + let mut named = IndexMap::new(); + named.insert("major".to_string(), NamedType::Switch); + named.insert("minor".to_string(), NamedType::Switch); + named.insert("patch".to_string(), NamedType::Switch); + Ok(CommandConfig { name: "inc".to_string(), positional: vec![PositionalType::optional_any("Field")], is_filter: true, is_sink: false, - named: IndexMap::new(), + named, rest_positional: true, }) } fn begin_filter(&mut self, args: Args) -> Result<(), ShellError> { + if args.has("major") { + self.major = true; + } + if args.has("minor") { + self.minor = true; + } + if args.has("patch") { + self.patch = true; + } + if let Some(args) = args.positional { for arg in args { match arg { @@ -93,7 +133,7 @@ impl Plugin for Inc { } fn filter(&mut self, input: Spanned) -> Result, ShellError> { - Ok(vec![ReturnSuccess::value(Inc::inc(input, &self.field)?)]) + Ok(vec![ReturnSuccess::value(self.inc(input, &self.field)?)]) } } From 0d6881383d9113e5f4550e4197f6730ee0e41ef1 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 18 Jul 2019 13:57:58 +1200 Subject: [PATCH 3/3] Add some tests --- tests/filters_test.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/filters_test.rs b/tests/filters_test.rs index 98ec17298..c9eb4baff 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -51,6 +51,28 @@ fn can_split_by_column() { assert_eq!(output, "name"); } +#[test] +fn can_inc_version() { + nu!( + output, + cwd("tests/fixtures/formats"), + "open cargo_sample.toml | inc package.version --minor | get package.version | echo $it" + ); + + assert_eq!(output, "0.2.0"); +} + +#[test] +fn can_inc_field() { + nu!( + output, + cwd("tests/fixtures/formats"), + "open cargo_sample.toml | inc package.edition | get package.edition | echo $it" + ); + + assert_eq!(output, "2019"); +} + #[test] fn can_filter_by_unit_size_comparison() { nu!(