From 1d356276c2d6f21bd330a7025fc164caedd4a878 Mon Sep 17 00:00:00 2001 From: Fernando Herrera Date: Thu, 4 Nov 2021 22:04:21 +0000 Subject: [PATCH] simple inc plugin implementation --- Cargo.lock | 43 +++++++++ Cargo.toml | 1 + crates/nu-plugin/src/plugin.rs | 6 +- crates/nu_plugin_inc/cargo.toml | 20 +++++ crates/nu_plugin_inc/src/inc.rs | 138 +++++++++++++++++++++++++++++ crates/nu_plugin_inc/src/lib.rs | 4 + crates/nu_plugin_inc/src/main.rs | 6 ++ crates/nu_plugin_inc/src/nu/mod.rs | 47 ++++++++++ 8 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 crates/nu_plugin_inc/cargo.toml create mode 100644 crates/nu_plugin_inc/src/inc.rs create mode 100644 crates/nu_plugin_inc/src/lib.rs create mode 100644 crates/nu_plugin_inc/src/main.rs create mode 100644 crates/nu_plugin_inc/src/nu/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 6854f7bb86..d38d6ea450 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -762,6 +762,16 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "nu_plugin_inc" +version = "0.1.0" +dependencies = [ + "nu-engine", + "nu-plugin", + "nu-protocol", + "semver", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -861,6 +871,15 @@ dependencies = [ "regex", ] +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + [[package]] name = "phf" version = "0.10.0" @@ -1125,6 +1144,24 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.130" @@ -1393,6 +1430,12 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "uncased" version = "0.9.6" diff --git a/Cargo.toml b/Cargo.toml index fe99034f7e..c7ab76ab85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "crates/nu-command", "crates/nu-protocol", "crates/nu-plugin", + "crates/nu_plugin_inc", ] [dependencies] diff --git a/crates/nu-plugin/src/plugin.rs b/crates/nu-plugin/src/plugin.rs index b6d3960a1c..efa14094db 100644 --- a/crates/nu-plugin/src/plugin.rs +++ b/crates/nu-plugin/src/plugin.rs @@ -38,6 +38,7 @@ pub enum PluginError { UnableToSpawn(String), EncodingError(String), DecodingError(String), + RunTimeError(String), } impl Display for PluginError { @@ -54,6 +55,9 @@ impl Display for PluginError { PluginError::DecodingError(err) => { write!(f, "error while decoding: {}", err) } + PluginError::RunTimeError(err) => { + write!(f, "runtime error: {}", err) + } } } } @@ -224,7 +228,7 @@ impl Command for PluginDeclaration { /// The `Plugin` trait defines the API which plugins use to "hook" into nushell. pub trait Plugin { fn signature(&self) -> Vec; - fn run(&self, name: &str, call: &Call, input: &Value) -> Result; + fn run(&mut self, name: &str, call: &Call, input: &Value) -> Result; } // Function used in the plugin definition for the communication protocol between diff --git a/crates/nu_plugin_inc/cargo.toml b/crates/nu_plugin_inc/cargo.toml new file mode 100644 index 0000000000..48be64c782 --- /dev/null +++ b/crates/nu_plugin_inc/cargo.toml @@ -0,0 +1,20 @@ +[package] +authors = ["The Nu Project Contributors"] +description = "A version incrementer plugin for Nushell" +edition = "2018" +license = "MIT" +name = "nu_plugin_inc" +version = "0.1.0" + +[lib] +doctest = false + +[dependencies] +nu-plugin = { path="../nu-plugin", version = "0.1.0" } +nu-protocol = { path="../nu-protocol", version = "0.1.0" } +nu-engine = { path="../nu-engine", version = "0.1.0" } + +semver = "0.11.0" + + +[build-dependencies] diff --git a/crates/nu_plugin_inc/src/inc.rs b/crates/nu_plugin_inc/src/inc.rs new file mode 100644 index 0000000000..c0327b39be --- /dev/null +++ b/crates/nu_plugin_inc/src/inc.rs @@ -0,0 +1,138 @@ +use nu_plugin::plugin::PluginError; +use nu_protocol::{Span, Value}; + +#[derive(Debug, Eq, PartialEq)] +pub enum Action { + SemVerAction(SemVerAction), + Default, +} + +#[derive(Debug, Eq, PartialEq)] +pub enum SemVerAction { + Major, + Minor, + Patch, +} + +#[derive(Default)] +pub struct Inc { + pub error: Option, + pub action: Option, +} + +impl Inc { + pub fn new() -> Self { + Default::default() + } + + fn apply(&self, input: &str) -> Value { + match &self.action { + Some(Action::SemVerAction(act_on)) => { + let mut ver = match semver::Version::parse(input) { + Ok(parsed_ver) => parsed_ver, + Err(_) => { + return Value::String { + val: input.to_string(), + span: Span::unknown(), + } + } + }; + + match act_on { + SemVerAction::Major => ver.increment_major(), + SemVerAction::Minor => ver.increment_minor(), + SemVerAction::Patch => ver.increment_patch(), + } + + Value::String { + val: ver.to_string(), + span: Span::unknown(), + } + } + Some(Action::Default) | None => match input.parse::() { + Ok(v) => Value::String { + val: (v + 1).to_string(), + span: Span::unknown(), + }, + Err(_) => Value::String { + val: input.to_string(), + span: Span::unknown(), + }, + }, + } + } + + pub fn for_semver(&mut self, part: SemVerAction) { + if self.permit() { + self.action = Some(Action::SemVerAction(part)); + } else { + self.log_error("can only apply one"); + } + } + + fn permit(&mut self) -> bool { + self.action.is_none() + } + + fn log_error(&mut self, message: &str) { + self.error = Some(message.to_string()); + } + + pub fn usage() -> &'static str { + "Usage: inc field [--major|--minor|--patch]" + } + + pub fn inc(&self, value: &Value) -> Result { + match value { + Value::Int { val, span } => Ok(Value::Int { + val: val + 1, + span: span.clone(), + }), + Value::String { val, .. } => Ok(self.apply(val)), + _ => Err(PluginError::RunTimeError("incrementable value".to_string())), + } + } +} + +#[cfg(test)] +mod tests { + mod semver { + use nu_protocol::{Span, Value}; + + use crate::inc::SemVerAction; + use crate::Inc; + + #[test] + fn major() { + let expected = Value::String { + val: "1.0.0".to_string(), + span: Span::unknown(), + }; + let mut inc = Inc::new(); + inc.for_semver(SemVerAction::Major); + assert_eq!(inc.apply("0.1.3"), expected) + } + + #[test] + fn minor() { + let expected = Value::String { + val: "0.2.0".to_string(), + span: Span::unknown(), + }; + let mut inc = Inc::new(); + inc.for_semver(SemVerAction::Minor); + assert_eq!(inc.apply("0.1.3"), expected) + } + + #[test] + fn patch() { + let expected = Value::String { + val: "0.1.4".to_string(), + span: Span::unknown(), + }; + let mut inc = Inc::new(); + inc.for_semver(SemVerAction::Patch); + assert_eq!(inc.apply("0.1.3"), expected) + } + } +} diff --git a/crates/nu_plugin_inc/src/lib.rs b/crates/nu_plugin_inc/src/lib.rs new file mode 100644 index 0000000000..e5428d8e6f --- /dev/null +++ b/crates/nu_plugin_inc/src/lib.rs @@ -0,0 +1,4 @@ +mod inc; +mod nu; + +pub use inc::Inc; diff --git a/crates/nu_plugin_inc/src/main.rs b/crates/nu_plugin_inc/src/main.rs new file mode 100644 index 0000000000..7245f1fbca --- /dev/null +++ b/crates/nu_plugin_inc/src/main.rs @@ -0,0 +1,6 @@ +use nu_plugin::serve_plugin; +use nu_plugin_inc::Inc; + +fn main() { + serve_plugin(&mut Inc::new()) +} diff --git a/crates/nu_plugin_inc/src/nu/mod.rs b/crates/nu_plugin_inc/src/nu/mod.rs new file mode 100644 index 0000000000..8eaf2fc9f0 --- /dev/null +++ b/crates/nu_plugin_inc/src/nu/mod.rs @@ -0,0 +1,47 @@ +use crate::inc::SemVerAction; +use crate::Inc; +use nu_plugin::{plugin::PluginError, Plugin}; +use nu_protocol::ast::Call; +use nu_protocol::{Signature, Span, Value}; + +impl Plugin for Inc { + fn signature(&self) -> Vec { + vec![Signature::build("inc") + .desc("Increment a value or version. Optionally use the column of a table.") + .switch( + "major", + "increment the major version (eg 1.2.1 -> 2.0.0)", + Some('M'), + ) + .switch( + "minor", + "increment the minor version (eg 1.2.1 -> 1.3.0)", + Some('m'), + ) + .switch( + "patch", + "increment the patch version (eg 1.2.1 -> 1.2.2)", + Some('p'), + )] + } + + fn run(&mut self, name: &str, call: &Call, input: &Value) -> Result { + if name != "inc" { + return Ok(Value::Nothing { + span: Span::unknown(), + }); + } + + if call.has_flag("major") { + self.for_semver(SemVerAction::Major); + } + if call.has_flag("minor") { + self.for_semver(SemVerAction::Minor); + } + if call.has_flag("patch") { + self.for_semver(SemVerAction::Patch); + } + + self.inc(input) + } +}