diff --git a/Cargo.toml b/Cargo.toml index 8a92ba7bd1..7222fa8a2e 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 62507ed31f..310281ddde 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 fdc6c8a6c0..d368205856 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 0000000000..b2d7cbfb34 --- /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 14e5b46f86..bf07898a2a 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 7abedd96b3..c8f2485287 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 8feebaa0ab..d76af0fe9c 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" } }