From d5704808d4be4011ff9d36cd63ffc41ea384c3f1 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 27 Jun 2019 16:56:48 +1200 Subject: [PATCH 1/3] First working plugin --- Cargo.lock | 25 +++++----- Cargo.toml | 21 ++++++-- src/cli.rs | 34 +------------ src/commands.rs | 3 +- src/commands/command.rs | 5 +- src/commands/plugin_inc.rs | 97 +++++++++++++++++++++++++++++++++++ src/errors.rs | 2 +- src/lib.rs | 26 ++++++++++ src/main.rs | 18 +------ src/object/base.rs | 22 ++++---- src/parser.rs | 2 +- src/parser/parse/span.rs | 4 +- src/plugins/inc.rs | 100 +++++++++++++++++++++++++++++++++++++ 13 files changed, 276 insertions(+), 83 deletions(-) create mode 100644 src/commands/plugin_inc.rs create mode 100644 src/lib.rs create mode 100644 src/plugins/inc.rs diff --git a/Cargo.lock b/Cargo.lock index 5af52f7e7f..230d1dd2b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,9 +188,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "chrono" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.93 (registry+https://github.com/rust-lang/crates.io-index)", @@ -202,7 +203,7 @@ name = "chrono-humanize" version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -210,7 +211,7 @@ name = "chrono-tz" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parse-zoneinfo 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -703,7 +704,7 @@ dependencies = [ [[package]] name = "enum-utils" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "enum-utils-from-str 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1442,7 +1443,7 @@ dependencies = [ "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "byte-unit 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "chrono-tz 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1452,7 +1453,7 @@ dependencies = [ "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "enum-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "enum-utils 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "enum_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1486,7 +1487,7 @@ dependencies = [ "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", "subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "sys-info 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", - "sysinfo 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", + "sysinfo 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "toml-query 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1787,7 +1788,7 @@ name = "pretty_env_logger" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2467,7 +2468,7 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2994,7 +2995,7 @@ dependencies = [ "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" +"checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe" "checksum chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2ff48a655fe8d2dae9a39e66af7fd8ff32a879e8c4e27422c25596a8b5e90d" "checksum chrono-tz 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e0e430fad0384e4defc3dc6b1223d1b886087a8bf9b7080e5ae027f73851ea15" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" @@ -3050,7 +3051,7 @@ dependencies = [ "checksum encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" "checksum encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" "checksum encoding_rs 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4155785c79f2f6701f185eb2e6b4caf0555ec03477cb4c70db67b465311620ed" -"checksum enum-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23c5d9343285feb320b4f2b0d0ba9501c11e1a19052026852d772a6e96e8de5d" +"checksum enum-utils 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f1ae672d9891879fb93e17ab6015c4e3bbe63fbeb23a41b9ac39ffa845b8836" "checksum enum-utils-from-str 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b5669381f76d7320e122abdd4a8307f986634f6d067fb69e31179422175801a" "checksum enum_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "406ac2a8c9eedf8af9ee1489bee9e50029278a6456c740f7454cf8a158abc816" "checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a" @@ -3237,7 +3238,7 @@ dependencies = [ "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e80b8831c5a543192ffc3727f01cf0e57579c6ac15558e3048bfb5708892167b" "checksum sys-info 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "76d6cf7b349b6a6daaf7a3797227e2f4108c8dd398e0aca7e29b9fb239948541" -"checksum sysinfo 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d291d07ba27acd519287ca22fb1fb024dcc4b925cddb63d69af24db153ca2c82" +"checksum sysinfo 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3e2cab189e59f72710e3dd5e1e0d5be0f6c5c999c326f2fdcdf3bf4483ec9fd" "checksum tempfile 3.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7dc4738f2e68ed2855de5ac9cdbe05c9216773ecde4739b2f095002ab03a13ef" "checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" diff --git a/Cargo.toml b/Cargo.toml index 20c2b12f2b..6f7d31f0ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,5 @@ +cargo-features = ["default-run"] + [package] name = "nu" version = "0.1.2" @@ -5,13 +7,14 @@ authors = ["Yehuda Katz ", "Jonathan Turner Result<(), Box> { command("get", get::get), command("enter", enter::enter), command("exit", exit::exit), + command("inc", plugin_inc::plugin_inc), command("lines", lines::lines), command("pick", pick::pick), command("split-column", split_column::split_column), @@ -170,39 +171,6 @@ pub async fn cli() -> Result<(), Box> { &language_reporting::DefaultConfig, ) .unwrap(); - - // match err { - // ShellError::Diagnostic(diag) => { - // let host = context.host.lock().unwrap(); - // let writer = host.err_termcolor(); - // line.push_str(" "); - // let files = crate::parser::Files::new(line); - - // language_reporting::emit( - // &mut writer.lock(), - // &files, - // &diag.diagnostic, - // &language_reporting::DefaultConfig, - // ) - // .unwrap(); - // } - - // ShellError::TypeError(desc) => context - // .host - // .lock() - // .unwrap() - // .stdout(&format!("TypeError: {}", desc)), - - // ShellError::MissingProperty { subpath, .. } => context - // .host - // .lock() - // .unwrap() - // .stdout(&format!("Missing property {}", subpath)), - - // ShellError::String(_) => { - // context.host.lock().unwrap().stdout(&format!("{}", err)) - // } - // } } LineResult::Break => { diff --git a/src/commands.rs b/src/commands.rs index 3454a0c01d..9d4f94d683 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -18,6 +18,7 @@ crate mod lines; crate mod ls; crate mod open; crate mod pick; +crate mod plugin_inc; crate mod ps; crate mod reject; crate mod save; @@ -42,5 +43,5 @@ crate mod where_; crate use command::command; crate use config::Config; crate use open::Open; -crate use where_::Where; crate use skip_while::SkipWhile; +crate use where_::Where; diff --git a/src/commands/command.rs b/src/commands/command.rs index d25cf52b67..36e521de67 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -6,6 +6,7 @@ use crate::parser::{ }; use crate::prelude::*; use getset::Getters; +use serde::{Deserialize, Serialize}; use std::path::PathBuf; #[derive(Getters)] @@ -51,14 +52,14 @@ pub struct SinkCommandArgs { pub input: Vec, } -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub enum CommandAction { ChangePath(PathBuf), Enter(Value), Exit, } -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub enum ReturnValue { Value(Value), Action(CommandAction), diff --git a/src/commands/plugin_inc.rs b/src/commands/plugin_inc.rs new file mode 100644 index 0000000000..3fbcba8424 --- /dev/null +++ b/src/commands/plugin_inc.rs @@ -0,0 +1,97 @@ +use crate::errors::ShellError; +use crate::parser::registry::{CommandConfig, PositionalType}; +use crate::parser::Spanned; +use crate::prelude::*; +use serde::{self, Deserialize, Serialize}; +use std::io::prelude::*; +use std::io::BufReader; +use std::io::{Read, Write}; +use subprocess::Exec; + +#[derive(Debug, Serialize, Deserialize)] +pub struct JsonRpc { + jsonrpc: String, + pub method: String, + pub params: T, +} +impl JsonRpc { + pub fn new>(method: U, params: T) -> Self { + JsonRpc { + jsonrpc: "2.0".into(), + method: method.into(), + params, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "method")] +#[allow(non_camel_case_types)] +pub enum NuResult { + response { params: VecDeque }, +} + +pub fn plugin_inc(args: CommandArgs) -> Result { + let input = args.input; + let args = if let Some(ref positional) = args.args.positional { + positional.clone() + } else { + vec![] + }; + + let stream = input + .map(move |v| { + let mut process = Exec::cmd("./target/debug/nu_plugin_inc"); + process = process.stdout(subprocess::Redirection::Pipe); + process = process.stdin(subprocess::Redirection::Pipe); + + let mut popen = process.popen().unwrap(); + + let mut stdout = popen.stdout.take().unwrap(); + let mut stdin = popen.stdin.take().unwrap(); + let mut reader = BufReader::new(stdout); + + let request = JsonRpc::new("init", args.clone()); + let request_raw = serde_json::to_string(&request).unwrap(); + stdin.write(format!("{}\n", request_raw).as_bytes()); + + // println!("Wrote out init"); + // println!("{:?}", v); + let request = JsonRpc::new("filter", v); + let request_raw = serde_json::to_string(&request).unwrap(); + stdin.write(format!("{}\n", request_raw).as_bytes()); + + 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 }) => { + let request: JsonRpc> = + JsonRpc::new("quit", vec![]); + let request_raw = serde_json::to_string(&request).unwrap(); + stdin.write(format!("{}\n", request_raw).as_bytes()); + params + } + Err(_) => { + let mut result = VecDeque::new(); + result.push_back(ReturnValue::Value(Value::Error(Box::new( + ShellError::string("Error while processing input"), + )))); + result + } + } + } + Err(x) => { + let mut result = VecDeque::new(); + result.push_back(ReturnValue::Value(Value::Error(Box::new( + ShellError::string("Error while processing input"), + )))); + result + } + } + }) + .flatten(); + + Ok(stream.boxed()) +} diff --git a/src/errors.rs b/src/errors.rs index 506aaaa0c6..8472e82dcc 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -151,7 +151,7 @@ impl ShellError { } } - crate fn string(title: impl Into) -> ShellError { + pub fn string(title: impl Into) -> ShellError { ShellError::String(StringError::new(title.into(), Value::nothing())) } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000..caf1dd8e59 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,26 @@ +#![feature(crate_visibility_modifier)] +#![feature(in_band_lifetimes)] +#![feature(async_await)] +#![feature(try_trait)] +#![feature(bind_by_move_pattern_guards)] + +mod cli; +mod commands; +mod context; +mod env; +mod errors; +mod evaluate; +mod format; +mod git; +mod object; +mod parser; +mod prelude; +mod shell; +mod stream; + +pub use crate::commands::command::ReturnValue; +pub use crate::parser::Spanned; +pub use cli::cli; +pub use errors::ShellError; +pub use object::base::{Primitive, Value}; +pub use parser::parse::text::Text; diff --git a/src/main.rs b/src/main.rs index 6cc0d21711..afb0bb32a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,26 +4,10 @@ #![feature(try_trait)] #![feature(bind_by_move_pattern_guards)] -mod cli; -mod commands; -mod context; -mod env; -mod errors; -mod evaluate; -mod format; -mod git; -mod object; -mod parser; -mod prelude; -mod shell; -mod stream; - use clap::{App, Arg}; use log::LevelFilter; use std::error::Error; -crate use parser::parse::text::Text; - fn main() -> Result<(), Box> { let matches = App::new("nu shell") .version("0.5") @@ -72,6 +56,6 @@ fn main() -> Result<(), Box> { builder.try_init()?; - futures::executor::block_on(crate::cli::cli())?; + futures::executor::block_on(nu::cli())?; Ok(()) } diff --git a/src/object/base.rs b/src/object/base.rs index 2a2a5b3c8d..ac94427d79 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -398,36 +398,36 @@ impl Value { } #[allow(unused)] - crate fn block(e: hir::Expression, source: Text) -> Value { + pub fn block(e: hir::Expression, source: Text) -> Value { Value::Block(Block::new(e, source)) } - crate fn string(s: impl Into) -> Value { + pub fn string(s: impl Into) -> Value { Value::Primitive(Primitive::String(s.into())) } - crate fn bytes(s: impl Into) -> Value { + pub fn bytes(s: impl Into) -> Value { Value::Primitive(Primitive::Bytes(s.into())) } - crate fn int(s: impl Into) -> Value { + pub fn int(s: impl Into) -> Value { Value::Primitive(Primitive::Int(s.into())) } - crate fn float(s: impl Into) -> Value { + pub fn float(s: impl Into) -> Value { Value::Primitive(Primitive::Float(s.into())) } - crate fn boolean(s: impl Into) -> Value { + pub fn boolean(s: impl Into) -> Value { Value::Primitive(Primitive::Boolean(s.into())) } - crate fn system_date(s: SystemTime) -> Value { + pub fn system_date(s: SystemTime) -> Value { Value::Primitive(Primitive::Date(s.into())) } #[allow(unused)] - crate fn date_from_str(s: &str) -> Result { + pub fn date_from_str(s: &str) -> Result { let date = DateTime::parse_from_rfc3339(s) .map_err(|err| ShellError::string(&format!("Date parse error: {}", err)))?; @@ -437,19 +437,19 @@ impl Value { } #[allow(unused)] - crate fn system_date_result(s: Result) -> Value { + pub fn system_date_result(s: Result) -> Value { match s { Ok(time) => Value::Primitive(Primitive::Date(time.into())), Err(err) => Value::Error(Box::new(ShellError::string(format!("{}", err)))), } } - crate fn nothing() -> Value { + pub fn nothing() -> Value { Value::Primitive(Primitive::Nothing) } #[allow(unused)] - crate fn list(values: impl Into>) -> Value { + pub fn list(values: impl Into>) -> Value { Value::List(values.into()) } } diff --git a/src/parser.rs b/src/parser.rs index e27cfce9ba..db584f4327 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -12,7 +12,7 @@ crate use parse::flag::Flag; crate use parse::operator::Operator; crate use parse::parser::{nom_input, pipeline}; crate use parse::pipeline::{Pipeline, PipelineElement}; -crate use parse::span::{Span, Spanned}; +pub use parse::span::{Span, Spanned}; crate use parse::text::Text; crate use parse::token_tree::TokenNode; crate use parse::tokens::{RawToken, Token}; diff --git a/src/parser/parse/span.rs b/src/parser/parse/span.rs index ca279eeaa2..1777ff7424 100644 --- a/src/parser/parse/span.rs +++ b/src/parser/parse/span.rs @@ -8,8 +8,8 @@ use serde_derive::{Deserialize, Serialize}; )] #[get = "crate"] pub struct Spanned { - crate span: Span, - crate item: T, + pub span: Span, + pub item: T, } impl std::ops::Deref for Spanned { diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs new file mode 100644 index 0000000000..c7cacaae8d --- /dev/null +++ b/src/plugins/inc.rs @@ -0,0 +1,100 @@ +use nu::{Primitive, ReturnValue, ShellError, Spanned, Value}; +use serde::{Deserialize, Serialize}; +use std::io; + +/// A wrapper for proactive notifications to the IDE (eg. diagnostics). These must +/// follow the JSON 2.0 RPC spec + +#[derive(Debug, Serialize, Deserialize)] +pub struct JsonRpc { + jsonrpc: String, + pub method: String, + pub params: T, +} +impl JsonRpc { + pub fn new>(method: U, params: T) -> Self { + JsonRpc { + jsonrpc: "2.0".into(), + method: method.into(), + params, + } + } +} + +fn send_response(result: T) { + let response = JsonRpc::new("response", result); + let response_raw = serde_json::to_string(&response).unwrap(); + println!("{}", response_raw); +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "method")] +#[allow(non_camel_case_types)] +pub enum NuCommand { + init { params: Vec> }, + filter { params: Value }, + quit, +} + +fn main() -> Result<(), Box> { + let mut inc_by = 1; + + loop { + let mut input = String::new(); + match io::stdin().read_line(&mut input) { + Ok(_) => { + let command = serde_json::from_str::(&input); + + match command { + Ok(NuCommand::init { params }) => { + for param in params { + match param { + Spanned { + item: Value::Primitive(Primitive::Int(i)), + .. + } => { + inc_by = i; + } + _ => { + send_response(ReturnValue::Value(Value::Error(Box::new( + ShellError::string("Unrecognized type in params"), + )))); + } + } + } + } + Ok(NuCommand::filter { params }) => match params { + Value::Primitive(Primitive::Int(i)) => { + send_response(vec![ReturnValue::Value(Value::int(i + inc_by))]); + } + Value::Primitive(Primitive::Bytes(b)) => { + send_response(vec![ReturnValue::Value(Value::bytes( + b + inc_by as u128, + ))]); + } + _ => { + send_response(ReturnValue::Value(Value::Error(Box::new( + ShellError::string("Unrecognized type in stream"), + )))); + } + }, + Ok(NuCommand::quit) => { + break; + } + Err(_) => { + send_response(ReturnValue::Value(Value::Error(Box::new( + ShellError::string("Unrecognized type in stream"), + )))); + } + } + } + Err(error) => { + send_response(ReturnValue::Value(Value::Error(Box::new( + ShellError::string("Unrecognized type in stream"), + )))); + } + } + } + + Ok(()) +} From 78bb5647fcde07d0fcb44168065c2ea66b4226ed Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 27 Jun 2019 17:16:29 +1200 Subject: [PATCH 2/3] A bit of tidying --- src/commands/plugin_inc.rs | 2 -- src/plugins/inc.rs | 27 +++++++++++++-------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/commands/plugin_inc.rs b/src/commands/plugin_inc.rs index 3fbcba8424..55ae2c3e92 100644 --- a/src/commands/plugin_inc.rs +++ b/src/commands/plugin_inc.rs @@ -55,8 +55,6 @@ pub fn plugin_inc(args: CommandArgs) -> Result { let request_raw = serde_json::to_string(&request).unwrap(); stdin.write(format!("{}\n", request_raw).as_bytes()); - // println!("Wrote out init"); - // println!("{:?}", v); let request = JsonRpc::new("filter", v); let request_raw = serde_json::to_string(&request).unwrap(); stdin.write(format!("{}\n", request_raw).as_bytes()); diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index c7cacaae8d..98d1b95f34 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -9,10 +9,10 @@ use std::io; pub struct JsonRpc { jsonrpc: String, pub method: String, - pub params: T, + pub params: Vec, } impl JsonRpc { - pub fn new>(method: U, params: T) -> Self { + pub fn new>(method: U, params: Vec) -> Self { JsonRpc { jsonrpc: "2.0".into(), method: method.into(), @@ -21,12 +21,11 @@ impl JsonRpc { } } -fn send_response(result: T) { +fn send_response(result: Vec) { let response = JsonRpc::new("response", result); let response_raw = serde_json::to_string(&response).unwrap(); println!("{}", response_raw); } - #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "method")] #[allow(non_camel_case_types)] @@ -56,9 +55,9 @@ fn main() -> Result<(), Box> { inc_by = i; } _ => { - send_response(ReturnValue::Value(Value::Error(Box::new( - ShellError::string("Unrecognized type in params"), - )))); + send_response(vec![ReturnValue::Value(Value::Error( + Box::new(ShellError::string("Unrecognized type in params")), + ))]); } } } @@ -73,25 +72,25 @@ fn main() -> Result<(), Box> { ))]); } _ => { - send_response(ReturnValue::Value(Value::Error(Box::new( + send_response(vec![ReturnValue::Value(Value::Error(Box::new( ShellError::string("Unrecognized type in stream"), - )))); + )))]); } }, Ok(NuCommand::quit) => { break; } Err(_) => { - send_response(ReturnValue::Value(Value::Error(Box::new( + send_response(vec![ReturnValue::Value(Value::Error(Box::new( ShellError::string("Unrecognized type in stream"), - )))); + )))]); } } } - Err(error) => { - send_response(ReturnValue::Value(Value::Error(Box::new( + Err(_) => { + send_response(vec![ReturnValue::Value(Value::Error(Box::new( ShellError::string("Unrecognized type in stream"), - )))); + )))]); } } } From 47f23cacc7333a1e08de3d662bbaabf58ab531b8 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 28 Jun 2019 04:47:24 +1200 Subject: [PATCH 3/3] Add second plugin --- Cargo.toml | 4 ++ src/cli.rs | 3 +- src/commands.rs | 2 +- src/commands/plugin.rs | 117 +++++++++++++++++++++++++++++++++++++ src/commands/plugin_inc.rs | 95 ------------------------------ src/object/base.rs | 6 ++ src/plugins/sum.rs | 83 ++++++++++++++++++++++++++ src/prelude.rs | 2 +- 8 files changed, 214 insertions(+), 98 deletions(-) create mode 100644 src/commands/plugin.rs delete mode 100644 src/commands/plugin_inc.rs create mode 100644 src/plugins/sum.rs diff --git a/Cargo.toml b/Cargo.toml index 6f7d31f0ec..56f6eee90f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,10 @@ pretty_assertions = "0.6.1" name = "nu" path = "src/lib.rs" +[[bin]] +name = "nu_plugin_sum" +path = "src/plugins/sum.rs" + [[bin]] name = "nu_plugin_inc" path = "src/plugins/inc.rs" diff --git a/src/cli.rs b/src/cli.rs index b1550165d9..27bbf83443 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -64,7 +64,6 @@ pub async fn cli() -> Result<(), Box> { command("get", get::get), command("enter", enter::enter), command("exit", exit::exit), - command("inc", plugin_inc::plugin_inc), command("lines", lines::lines), command("pick", pick::pick), command("split-column", split_column::split_column), @@ -82,6 +81,8 @@ pub async fn cli() -> Result<(), Box> { Arc::new(Config), Arc::new(SkipWhile), command("sort-by", sort_by::sort_by), + command("inc", |x| plugin::plugin("inc".into(), x)), + command("sum", |x| plugin::plugin("sum".into(), x)), ]); context.add_sinks(vec![ diff --git a/src/commands.rs b/src/commands.rs index 9d4f94d683..c5a9a4778c 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -18,7 +18,7 @@ crate mod lines; crate mod ls; crate mod open; crate mod pick; -crate mod plugin_inc; +crate mod plugin; crate mod ps; crate mod reject; crate mod save; diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs new file mode 100644 index 0000000000..b3b85a2bfd --- /dev/null +++ b/src/commands/plugin.rs @@ -0,0 +1,117 @@ +use crate::errors::ShellError; +use crate::parser::registry::{CommandConfig, PositionalType}; +use crate::parser::Spanned; +use crate::prelude::*; +use serde::{self, Deserialize, Serialize}; +use std::io::prelude::*; +use std::io::BufReader; +use std::io::{Read, Write}; +use subprocess::Exec; + +#[derive(Debug, Serialize, Deserialize)] +pub struct JsonRpc { + jsonrpc: String, + pub method: String, + pub params: T, +} +impl JsonRpc { + pub fn new>(method: U, params: T) -> Self { + JsonRpc { + jsonrpc: "2.0".into(), + method: method.into(), + params, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "method")] +#[allow(non_camel_case_types)] +pub enum NuResult { + response { params: VecDeque }, +} + +pub fn plugin(plugin_name: String, args: CommandArgs) -> Result { + let input = args.input; + let args = if let Some(ref positional) = args.args.positional { + positional.clone() + } else { + vec![] + }; + + let mut path = std::path::PathBuf::from("."); + path.push("target"); + path.push("debug"); + path.push(format!("nu_plugin_{}", plugin_name)); + let mut child = std::process::Command::new(path) + .stdin(std::process::Stdio::piped()) + .stdout(std::process::Stdio::piped()) + .spawn() + .expect("Failed to spawn child process"); + + { + let mut stdin = child.stdin.as_mut().expect("Failed to open stdin"); + let mut stdout = child.stdout.as_mut().expect("Failed to open stdout"); + + let mut reader = BufReader::new(stdout); + + let request = JsonRpc::new("init", args.clone()); + let request_raw = serde_json::to_string(&request).unwrap(); + stdin.write(format!("{}\n", request_raw).as_bytes()); + } + let mut eos = VecDeque::new(); + eos.push_back(Value::Primitive(Primitive::EndOfStream)); + + let stream = input + .chain(eos) + .map(move |v| match v { + Value::Primitive(Primitive::EndOfStream) => { + let mut stdin = child.stdin.as_mut().expect("Failed to open stdin"); + let mut stdout = child.stdout.as_mut().expect("Failed to open stdout"); + + let mut reader = BufReader::new(stdout); + let request: JsonRpc> = JsonRpc::new("quit", vec![]); + let request_raw = serde_json::to_string(&request).unwrap(); + stdin.write(format!("{}\n", request_raw).as_bytes()); + + VecDeque::new() + } + _ => { + let mut stdin = child.stdin.as_mut().expect("Failed to open stdin"); + let mut stdout = child.stdout.as_mut().expect("Failed to open stdout"); + + let mut reader = BufReader::new(stdout); + + let request = JsonRpc::new("filter", v); + let request_raw = serde_json::to_string(&request).unwrap(); + stdin.write(format!("{}\n", request_raw).as_bytes()); + + 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 }) => params, + Err(_) => { + let mut result = VecDeque::new(); + result.push_back(ReturnValue::Value(Value::Error(Box::new( + ShellError::string("Error while processing input"), + )))); + result + } + } + } + Err(x) => { + let mut result = VecDeque::new(); + result.push_back(ReturnValue::Value(Value::Error(Box::new( + ShellError::string("Error while processing input"), + )))); + result + } + } + } + }) + .flatten(); + + Ok(stream.boxed()) +} diff --git a/src/commands/plugin_inc.rs b/src/commands/plugin_inc.rs deleted file mode 100644 index 55ae2c3e92..0000000000 --- a/src/commands/plugin_inc.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::errors::ShellError; -use crate::parser::registry::{CommandConfig, PositionalType}; -use crate::parser::Spanned; -use crate::prelude::*; -use serde::{self, Deserialize, Serialize}; -use std::io::prelude::*; -use std::io::BufReader; -use std::io::{Read, Write}; -use subprocess::Exec; - -#[derive(Debug, Serialize, Deserialize)] -pub struct JsonRpc { - jsonrpc: String, - pub method: String, - pub params: T, -} -impl JsonRpc { - pub fn new>(method: U, params: T) -> Self { - JsonRpc { - jsonrpc: "2.0".into(), - method: method.into(), - params, - } - } -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(tag = "method")] -#[allow(non_camel_case_types)] -pub enum NuResult { - response { params: VecDeque }, -} - -pub fn plugin_inc(args: CommandArgs) -> Result { - let input = args.input; - let args = if let Some(ref positional) = args.args.positional { - positional.clone() - } else { - vec![] - }; - - let stream = input - .map(move |v| { - let mut process = Exec::cmd("./target/debug/nu_plugin_inc"); - process = process.stdout(subprocess::Redirection::Pipe); - process = process.stdin(subprocess::Redirection::Pipe); - - let mut popen = process.popen().unwrap(); - - let mut stdout = popen.stdout.take().unwrap(); - let mut stdin = popen.stdin.take().unwrap(); - let mut reader = BufReader::new(stdout); - - let request = JsonRpc::new("init", args.clone()); - let request_raw = serde_json::to_string(&request).unwrap(); - stdin.write(format!("{}\n", request_raw).as_bytes()); - - let request = JsonRpc::new("filter", v); - let request_raw = serde_json::to_string(&request).unwrap(); - stdin.write(format!("{}\n", request_raw).as_bytes()); - - 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 }) => { - let request: JsonRpc> = - JsonRpc::new("quit", vec![]); - let request_raw = serde_json::to_string(&request).unwrap(); - stdin.write(format!("{}\n", request_raw).as_bytes()); - params - } - Err(_) => { - let mut result = VecDeque::new(); - result.push_back(ReturnValue::Value(Value::Error(Box::new( - ShellError::string("Error while processing input"), - )))); - result - } - } - } - Err(x) => { - let mut result = VecDeque::new(); - result.push_back(ReturnValue::Value(Value::Error(Box::new( - ShellError::string("Error while processing input"), - )))); - result - } - } - }) - .flatten(); - - Ok(stream.boxed()) -} diff --git a/src/object/base.rs b/src/object/base.rs index ac94427d79..b07c431611 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -40,6 +40,8 @@ pub enum Primitive { String(String), Boolean(bool), Date(DateTime), + + EndOfStream, } impl Serialize for Primitive { @@ -49,6 +51,7 @@ impl Serialize for Primitive { { match self { Primitive::Nothing => serializer.serialize_i32(0), + Primitive::EndOfStream => serializer.serialize_i32(0), Primitive::Int(i) => serializer.serialize_i64(*i), Primitive::Float(OF64 { inner: f }) => serializer.serialize_f64(f.into_inner()), Primitive::Bytes(b) => serializer.serialize_u128(*b), @@ -65,6 +68,7 @@ impl Primitive { match self { Nothing => "nothing", + EndOfStream => "end-of-stream", Int(_) => "int", Float(_) => "float", Bytes(_) => "bytes", @@ -80,6 +84,7 @@ impl Primitive { match self { Nothing => write!(f, "Nothing"), + EndOfStream => write!(f, "EndOfStream"), Int(int) => write!(f, "{}", int), Float(float) => write!(f, "{:?}", float), Bytes(bytes) => write!(f, "{}", bytes), @@ -92,6 +97,7 @@ impl Primitive { crate fn format(&self, field_name: Option<&DataDescriptor>) -> String { match self { Primitive::Nothing => format!("{}", Color::Black.bold().paint("-")), + Primitive::EndOfStream => format!("{}", Color::Black.bold().paint("-")), Primitive::Bytes(b) => { let byte = byte_unit::Byte::from_bytes(*b); diff --git a/src/plugins/sum.rs b/src/plugins/sum.rs new file mode 100644 index 0000000000..431452dc21 --- /dev/null +++ b/src/plugins/sum.rs @@ -0,0 +1,83 @@ +use nu::{Primitive, ReturnValue, ShellError, Spanned, Value}; +use serde::{Deserialize, Serialize}; +use std::io; + +/// A wrapper for proactive notifications to the IDE (eg. diagnostics). These must +/// follow the JSON 2.0 RPC spec + +#[derive(Debug, Serialize, Deserialize)] +pub struct JsonRpc { + jsonrpc: String, + pub method: String, + pub params: Vec, +} +impl JsonRpc { + pub fn new>(method: U, params: Vec) -> Self { + JsonRpc { + jsonrpc: "2.0".into(), + method: method.into(), + params, + } + } +} + +fn send_response(result: Vec) { + let response = JsonRpc::new("response", result); + let response_raw = serde_json::to_string(&response).unwrap(); + println!("{}", response_raw); +} +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "method")] +#[allow(non_camel_case_types)] +pub enum NuCommand { + init { params: Vec> }, + filter { params: Value }, + quit, +} + +fn main() -> Result<(), Box> { + let mut total = 0i64; + + loop { + let mut input = String::new(); + match io::stdin().read_line(&mut input) { + Ok(_) => { + let command = serde_json::from_str::(&input); + + match command { + Ok(NuCommand::init { .. }) => {} + Ok(NuCommand::filter { params }) => match params { + Value::Primitive(Primitive::Int(i)) => { + total += i as i64; + send_response(vec![ReturnValue::Value(Value::int(total))]); + } + Value::Primitive(Primitive::Bytes(b)) => { + total += b as i64; + send_response(vec![ReturnValue::Value(Value::bytes(total as u128))]); + } + _ => { + send_response(vec![ReturnValue::Value(Value::Error(Box::new( + ShellError::string("Unrecognized type in stream"), + )))]); + } + }, + Ok(NuCommand::quit) => { + break; + } + Err(_) => { + send_response(vec![ReturnValue::Value(Value::Error(Box::new( + ShellError::string("Unrecognized type in stream"), + )))]); + } + } + } + Err(_) => { + send_response(vec![ReturnValue::Value(Value::Error(Box::new( + ShellError::string("Unrecognized type in stream"), + )))]); + } + } + } + + Ok(()) +} diff --git a/src/prelude.rs b/src/prelude.rs index 241b8aad4f..90b7945cb1 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -4,7 +4,7 @@ crate use crate::context::Context; crate use crate::env::host::handle_unexpected; crate use crate::env::{Environment, Host}; crate use crate::errors::ShellError; -crate use crate::object::Value; +crate use crate::object::{Primitive, Value}; crate use crate::stream::{single_output, InputStream, OutputStream}; crate use crate::Text; crate use futures::{FutureExt, StreamExt};