From e4797f889524ddef48127e8b92ddf170ed561795 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sat, 27 Jul 2019 06:40:00 +1200 Subject: [PATCH] Add end_plugin and sum --- Cargo.toml | 4 + src/commands/plugin.rs | 45 ++++++++- src/plugin.rs | 12 +++ src/plugins/sum.rs | 99 ++++++++++++++++++++ tests/filters_test.rs | 14 ++- tests/fixtures/formats/sample.ini | 2 +- tests/fixtures/formats/sgml_description.json | 4 + 7 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 src/plugins/sum.rs diff --git a/Cargo.toml b/Cargo.toml index 8a92ba7bd..7222fa8a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,6 +91,10 @@ path = "src/lib.rs" name = "nu_plugin_inc" path = "src/plugins/inc.rs" +[[bin]] +name = "nu_plugin_sum" +path = "src/plugins/sum.rs" + [[bin]] name = "nu_plugin_add" path = "src/plugins/add.rs" diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index 62507ed31..310281ddd 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -125,12 +125,51 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result> = JsonRpc::new("quit", vec![]); + let mut reader = BufReader::new(stdout); + + let request: JsonRpc> = JsonRpc::new("end_filter", vec![]); let request_raw = serde_json::to_string(&request).unwrap(); let _ = stdin.write(format!("{}\n", request_raw).as_bytes()); // TODO: Handle error - VecDeque::new() + let mut input = String::new(); + match reader.read_line(&mut input) { + Ok(_) => { + let response = serde_json::from_str::(&input); + match response { + Ok(NuResult::response { params }) => match params { + Ok(params) => { + let request: JsonRpc> = + JsonRpc::new("quit", vec![]); + let request_raw = serde_json::to_string(&request).unwrap(); + let _ = stdin.write(format!("{}\n", request_raw).as_bytes()); // TODO: Handle error + + params + } + Err(e) => { + let mut result = VecDeque::new(); + result.push_back(ReturnValue::Err(e)); + result + } + }, + Err(e) => { + let mut result = VecDeque::new(); + result.push_back(Err(ShellError::string(format!( + "Error while processing input: {:?} {}", + e, input + )))); + result + } + } + } + Err(e) => { + let mut result = VecDeque::new(); + result.push_back(Err(ShellError::string(format!( + "Error while processing input: {:?}", + e + )))); + result + } + } } _ => { let stdin = child.stdin.as_mut().expect("Failed to open stdin"); diff --git a/src/plugin.rs b/src/plugin.rs index fdc6c8a6c..d36820585 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -15,6 +15,10 @@ pub trait Plugin { Err(ShellError::string("`filter` not implemented in plugin")) } #[allow(unused)] + fn end_filter(&mut self) -> Result, ShellError> { + Ok(vec![]) + } + #[allow(unused)] fn sink(&mut self, call_info: CallInfo, input: Vec>) {} fn quit(&mut self) { @@ -42,6 +46,10 @@ pub fn serve_plugin(plugin: &mut dyn Plugin) { Ok(NuCommand::filter { params }) => { send_response(plugin.filter(params)); } + Ok(NuCommand::end_filter) => { + send_response(plugin.end_filter()); + } + Ok(NuCommand::sink { params }) => { plugin.sink(params.0, params.1); return; @@ -79,6 +87,9 @@ pub fn serve_plugin(plugin: &mut dyn Plugin) { Ok(NuCommand::filter { params }) => { send_response(plugin.filter(params)); } + Ok(NuCommand::end_filter) => { + send_response(plugin.end_filter()); + } Ok(NuCommand::sink { params }) => { plugin.sink(params.0, params.1); break; @@ -140,6 +151,7 @@ pub enum NuCommand { filter { params: Spanned, }, + end_filter, sink { params: (CallInfo, Vec>), }, diff --git a/src/plugins/sum.rs b/src/plugins/sum.rs new file mode 100644 index 000000000..b2d7cbfb3 --- /dev/null +++ b/src/plugins/sum.rs @@ -0,0 +1,99 @@ +use indexmap::IndexMap; +use nu::{ + serve_plugin, CallInfo, CommandConfig, Plugin, Primitive, ReturnSuccess, ReturnValue, + ShellError, Spanned, Value, +}; + +struct Sum { + total: Option>, +} +impl Sum { + fn new() -> Sum { + Sum { total: None } + } + + fn sum(&mut self, value: Spanned) -> Result<(), ShellError> { + match value.item { + Value::Primitive(Primitive::Int(i)) => { + match self.total { + Some(Spanned { + item: Value::Primitive(Primitive::Int(j)), + span, + }) => { + //TODO: handle overflow + self.total = Some(Spanned { + item: Value::int(i + j), + span, + }); + Ok(()) + } + None => { + self.total = Some(value); + Ok(()) + } + _ => Err(ShellError::string(format!( + "Could not sum non-integer or unrelated types" + ))), + } + } + Value::Primitive(Primitive::Bytes(b)) => { + match self.total { + Some(Spanned { + item: Value::Primitive(Primitive::Bytes(j)), + span, + }) => { + //TODO: handle overflow + self.total = Some(Spanned { + item: Value::bytes(b + j), + span, + }); + Ok(()) + } + None => { + self.total = Some(value); + Ok(()) + } + _ => Err(ShellError::string(format!( + "Could not sum non-integer or unrelated types" + ))), + } + } + x => Err(ShellError::string(format!( + "Unrecognized type in stream: {:?}", + x + ))), + } + } +} + +impl Plugin for Sum { + fn config(&mut self) -> Result { + Ok(CommandConfig { + name: "sum".to_string(), + positional: vec![], + is_filter: true, + is_sink: false, + named: IndexMap::new(), + rest_positional: true, + }) + } + fn begin_filter(&mut self, _: CallInfo) -> Result<(), ShellError> { + Ok(()) + } + + fn filter(&mut self, input: Spanned) -> Result, ShellError> { + self.sum(input)?; + Ok(vec![]) + } + + fn end_filter(&mut self) -> Result, ShellError> { + match self.total { + None => Ok(vec![]), + Some(ref v) => Ok(vec![ReturnSuccess::value(v.clone())]), + } + } +} + +fn main() { + serve_plugin(&mut Sum::new()); +} diff --git a/tests/filters_test.rs b/tests/filters_test.rs index 14e5b46f8..bf07898a2 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -2,10 +2,10 @@ mod helpers; use helpers::in_directory as cwd; - #[test] fn can_convert_table_to_csv_text_and_from_csv_text_back_into_table() { - nu!(output, + nu!( + output, cwd("tests/fixtures/formats"), "open caco3_plastics.csv | to-csv | from-csv | first 1 | get origin | echo $it" ); @@ -87,6 +87,16 @@ fn can_inc_field() { assert_eq!(output, "2019"); } +#[test] +fn can_sum() { + nu!( + output, + cwd("tests/fixtures/formats"), + "open sgml_description.json | get glossary.GlossDiv.GlossList.GlossEntry.Sections | sum | echo $it" + ); + + assert_eq!(output, "203") +} #[test] fn can_filter_by_unit_size_comparison() { nu!( diff --git a/tests/fixtures/formats/sample.ini b/tests/fixtures/formats/sample.ini index 7abedd96b..c8f248528 100644 --- a/tests/fixtures/formats/sample.ini +++ b/tests/fixtures/formats/sample.ini @@ -10,7 +10,7 @@ string2 = "Case 2" ; comment line key = new value -integer = 1234 +integer = 5678 real = 3.14 string1 = 'Case 1' string2 = "Case 2" diff --git a/tests/fixtures/formats/sgml_description.json b/tests/fixtures/formats/sgml_description.json index 8feebaa0a..d76af0fe9 100644 --- a/tests/fixtures/formats/sgml_description.json +++ b/tests/fixtures/formats/sgml_description.json @@ -18,6 +18,10 @@ "XML" ] }, + "Sections": [ + 101, + 102 + ], "GlossSee": "markup" } }