From 56307553ae4acb684130831ceb8b8bc828eaa3dd Mon Sep 17 00:00:00 2001 From: Fernando Herrera Date: Thu, 2 Dec 2021 05:42:56 +0000 Subject: [PATCH] Plugin with evaluated call (#393) * plugin trait * impl of trait * record and absolute path * plugin example crate * clippy error * correcting cargo * evaluated call for plugin --- Cargo.lock | 11 +- Cargo.toml | 1 + crates/nu-engine/src/call_ext.rs | 4 +- crates/nu-engine/src/lib.rs | 2 - crates/nu-parser/Cargo.toml | 5 +- crates/nu-parser/src/errors.rs | 6 +- crates/nu-parser/src/parse_keywords.rs | 90 +-- crates/nu-plugin/Cargo.toml | 1 + crates/nu-plugin/schema/plugin.capnp | 31 +- crates/nu-plugin/src/evaluated_call.rs | 158 +++++ crates/nu-plugin/src/lib.rs | 2 + crates/nu-plugin/src/plugin.rs | 161 +++-- crates/nu-plugin/src/plugin_call.rs | 99 ++- crates/nu-plugin/src/plugin_capnp.rs | 621 +++++++++--------- crates/nu-plugin/src/serializers/call.rs | 184 ++---- crates/nu-plugin/src/serializers/signature.rs | 111 +++- crates/nu-plugin/src/serializers/value.rs | 128 +++- crates/nu-protocol/src/engine/command.rs | 4 +- crates/nu-protocol/src/engine/engine_state.rs | 39 +- crates/nu-protocol/src/shell_error.rs | 3 - .../src/value}/from_value.rs | 56 +- crates/nu-protocol/src/value/mod.rs | 2 + crates/nu_plugin_example/Cargo.toml | 11 + crates/nu_plugin_example/README.md | 4 + crates/nu_plugin_example/src/main.rs | 145 ++++ crates/nu_plugin_inc/Cargo.toml | 3 +- crates/nu_plugin_inc/src/inc.rs | 7 +- crates/nu_plugin_inc/src/nu/mod.rs | 12 +- src/main.rs | 9 + 29 files changed, 1140 insertions(+), 770 deletions(-) create mode 100644 crates/nu-plugin/src/evaluated_call.rs rename crates/{nu-engine/src => nu-protocol/src/value}/from_value.rs (84%) create mode 100644 crates/nu_plugin_example/Cargo.toml create mode 100644 crates/nu_plugin_example/README.md create mode 100644 crates/nu_plugin_example/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index ca6149d8b7..785a1e56d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1503,7 +1503,6 @@ version = "0.1.0" dependencies = [ "miette", "nu-path", - "nu-plugin", "nu-protocol", "serde_json", "thiserror", @@ -1523,6 +1522,7 @@ version = "0.1.0" dependencies = [ "capnp", "capnpc", + "nu-engine", "nu-protocol", ] @@ -1563,11 +1563,18 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "nu_plugin_example" +version = "0.1.0" +dependencies = [ + "nu-plugin", + "nu-protocol", +] + [[package]] name = "nu_plugin_inc" version = "0.1.0" dependencies = [ - "nu-engine", "nu-plugin", "nu-protocol", "semver", diff --git a/Cargo.toml b/Cargo.toml index 6861e2a6eb..e2d90721e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "crates/nu-protocol", "crates/nu-plugin", "crates/nu_plugin_inc", + "crates/nu_plugin_example", ] [dependencies] diff --git a/crates/nu-engine/src/call_ext.rs b/crates/nu-engine/src/call_ext.rs index 256320b565..3ea04e0467 100644 --- a/crates/nu-engine/src/call_ext.rs +++ b/crates/nu-engine/src/call_ext.rs @@ -1,10 +1,10 @@ use nu_protocol::{ ast::Call, engine::{EngineState, Stack}, - ShellError, + FromValue, ShellError, }; -use crate::{eval_expression, FromValue}; +use crate::eval_expression; pub trait CallExt { fn get_flag( diff --git a/crates/nu-engine/src/lib.rs b/crates/nu-engine/src/lib.rs index cc7e7a1236..ab957a4638 100644 --- a/crates/nu-engine/src/lib.rs +++ b/crates/nu-engine/src/lib.rs @@ -1,9 +1,7 @@ mod call_ext; mod documentation; mod eval; -mod from_value; pub use call_ext::CallExt; pub use documentation::{generate_docs, get_brief_help, get_documentation, get_full_help}; pub use eval::{eval_block, eval_expression, eval_operator}; -pub use from_value::FromValue; diff --git a/crates/nu-parser/Cargo.toml b/crates/nu-parser/Cargo.toml index 6832482b49..1e75ea18b2 100644 --- a/crates/nu-parser/Cargo.toml +++ b/crates/nu-parser/Cargo.toml @@ -6,10 +6,9 @@ edition = "2018" [dependencies] miette = "3.0.0" thiserror = "1.0.29" -nu-protocol = { path = "../nu-protocol"} -nu-plugin = { path = "../nu-plugin", optional=true} serde_json = "1.0" nu-path = {path = "../nu-path"} +nu-protocol = { path = "../nu-protocol"} [features] -plugin = ["nu-plugin"] +plugin = [] diff --git a/crates/nu-parser/src/errors.rs b/crates/nu-parser/src/errors.rs index e56248ea66..8c6635b107 100644 --- a/crates/nu-parser/src/errors.rs +++ b/crates/nu-parser/src/errors.rs @@ -191,7 +191,7 @@ pub enum ParseError { #[diagnostic(code(nu::parser::export_not_found), url(docsrs))] FileNotFound(String), - #[error("Plugin error")] - #[diagnostic(code(nu::parser::plugin_error), url(docsrs))] - PluginError(String), + #[error("{0}")] + #[diagnostic()] + LabeledError(String, String, #[label("{1}")] Span), } diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index b1778cad39..4e5062436e 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -9,9 +9,6 @@ use nu_protocol::{ use std::collections::{HashMap, HashSet}; use std::path::Path; -#[cfg(feature = "plugin")] -use nu_plugin::plugin::{get_signature, PluginDeclaration}; - use crate::{ lex, lite_parse, parser::{ @@ -1091,6 +1088,9 @@ pub fn parse_plugin( working_set: &mut StateWorkingSet, spans: &[Span], ) -> (Statement, Option) { + use std::{path::PathBuf, str::FromStr}; + + use nu_path::canonicalize; use nu_protocol::Signature; let name = working_set.get_span_contents(spans[0]); @@ -1117,51 +1117,55 @@ pub fn parse_plugin( )), 2 => { let name_expr = working_set.get_span_contents(spans[1]); - if let Ok(filename) = String::from_utf8(name_expr.to_vec()) { - let source_file = Path::new(&filename); - - if source_file.exists() & source_file.is_file() { - // get signature from plugin - match get_signature(source_file) { - Err(err) => Some(ParseError::PluginError(format!("{}", err))), - Ok(signatures) => { - for signature in signatures { - // create plugin command declaration (need struct impl Command) - // store declaration in working set - let plugin_decl = - PluginDeclaration::new(filename.clone(), signature); - - working_set.add_plugin_decl(Box::new(plugin_decl)); - } - - None - } + String::from_utf8(name_expr.to_vec()) + .map_err(|_| ParseError::NonUtf8(spans[1])) + .and_then(|name| { + canonicalize(&name).map_err(|e| ParseError::FileNotFound(e.to_string())) + }) + .and_then(|path| { + if path.exists() & path.is_file() { + working_set.add_plugin_signature(path, None); + Ok(()) + } else { + Err(ParseError::FileNotFound(format!("{:?}", path))) } - } else { - Some(ParseError::FileNotFound(filename)) - } - } else { - Some(ParseError::NonUtf8(spans[1])) - } + }) + .err() } 3 => { - let filename = working_set.get_span_contents(spans[1]); + let filename_slice = working_set.get_span_contents(spans[1]); let signature = working_set.get_span_contents(spans[2]); + let mut path = PathBuf::new(); - if let Ok(filename) = String::from_utf8(filename.to_vec()) { - if let Ok(signature) = serde_json::from_slice::(signature) { - let plugin_decl = PluginDeclaration::new(filename, signature); - working_set.add_plugin_decl(Box::new(plugin_decl)); - - None - } else { - Some(ParseError::PluginError( - "unable to deserialize signature".into(), - )) - } - } else { - Some(ParseError::NonUtf8(spans[1])) - } + String::from_utf8(filename_slice.to_vec()) + .map_err(|_| ParseError::NonUtf8(spans[1])) + .and_then(|name| { + PathBuf::from_str(name.as_str()).map_err(|_| { + ParseError::InternalError( + format!("Unable to create path from string {}", name), + spans[0], + ) + }) + }) + .and_then(|path_inner| { + path = path_inner; + serde_json::from_slice::(signature).map_err(|_| { + ParseError::LabeledError( + "Signature deserialization error".into(), + "unable to deserialize signature".into(), + spans[0], + ) + }) + }) + .and_then(|signature| { + if path.exists() & path.is_file() { + working_set.add_plugin_signature(path, Some(signature)); + Ok(()) + } else { + Err(ParseError::FileNotFound(format!("{:?}", path))) + } + }) + .err() } _ => { let span = spans[3..].iter().fold(spans[3], |acc, next| Span { diff --git a/crates/nu-plugin/Cargo.toml b/crates/nu-plugin/Cargo.toml index cb5346eab7..df6e6236e3 100644 --- a/crates/nu-plugin/Cargo.toml +++ b/crates/nu-plugin/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] capnp = "0.14.3" nu-protocol = { path = "../nu-protocol" } +nu-engine = { path = "../nu-engine" } [build-dependencies] capnpc = "0.14.3" diff --git a/crates/nu-plugin/schema/plugin.capnp b/crates/nu-plugin/schema/plugin.capnp index 27b036d859..3b2a230832 100644 --- a/crates/nu-plugin/schema/plugin.capnp +++ b/crates/nu-plugin/schema/plugin.capnp @@ -41,9 +41,15 @@ struct Value { float @4 :Float64; string @5 :Text; list @6 :List(Value); + record @7: Record; } } +struct Record { + cols @0 :List(Text); + vals @1 :List(Value); +} + # Structs required to define the plugin signature struct Signature { name @0 :Text; @@ -98,32 +104,17 @@ enum Shape { boolean @5; } -# The next structs define the call information sent to th plugin -struct Expression { - union { - garbage @0 :Void; - bool @1 :Bool; - int @2 :Int64; - float @3 :Float64; - string @4 :Text; - list @5 :List(Expression); - # The expression list can be exteded based on the user need - # If a plugin requires something from the expression object, it - # will need to be added to this list - } -} - -struct Call { +struct EvaluatedCall { head @0: Span; - positional @1 :List(Expression); - # The expression in the map can be optional + positional @1 :List(Value); + # The value in the map can be optional # Check for existence when deserializing - named @2 :Map(Text, Expression); + named @2 :Map(Text, Value); } struct CallInfo { name @0: Text; - call @1: Call; + call @1: EvaluatedCall; input @2: Value; } diff --git a/crates/nu-plugin/src/evaluated_call.rs b/crates/nu-plugin/src/evaluated_call.rs new file mode 100644 index 0000000000..31a4f6333f --- /dev/null +++ b/crates/nu-plugin/src/evaluated_call.rs @@ -0,0 +1,158 @@ +use nu_engine::eval_expression; +use nu_protocol::{ + ast::Call, + engine::{EngineState, Stack}, + FromValue, ShellError, Span, Spanned, Value, +}; + +#[derive(Debug, Clone)] +pub struct EvaluatedCall { + pub head: Span, + pub positional: Vec, + pub named: Vec<(Spanned, Option)>, +} + +impl EvaluatedCall { + pub fn try_from_call( + call: &Call, + engine_state: &EngineState, + stack: &mut Stack, + ) -> Result { + let positional = call + .positional + .iter() + .map(|expr| eval_expression(engine_state, stack, expr)) + .collect::, ShellError>>()?; + + let mut named = Vec::with_capacity(call.named.len()); + for (string, expr) in call.named.iter() { + let value = match expr { + None => None, + Some(expr) => Some(eval_expression(engine_state, stack, expr)?), + }; + + named.push((string.clone(), value)) + } + + Ok(Self { + head: call.head, + positional, + named, + }) + } + + pub fn has_flag(&self, flag_name: &str) -> bool { + for name in &self.named { + if flag_name == name.0.item { + return true; + } + } + + false + } + + pub fn get_flag_value(&self, flag_name: &str) -> Option { + for name in &self.named { + if flag_name == name.0.item { + return name.1.clone(); + } + } + + None + } + + pub fn nth(&self, pos: usize) -> Option { + self.positional.get(pos).cloned() + } + + pub fn get_flag(&self, name: &str) -> Result, ShellError> { + if let Some(value) = self.get_flag_value(name) { + FromValue::from_value(&value).map(Some) + } else { + Ok(None) + } + } + + pub fn rest(&self, starting_pos: usize) -> Result, ShellError> { + self.positional + .iter() + .skip(starting_pos) + .map(|value| FromValue::from_value(value)) + .collect() + } + + pub fn opt(&self, pos: usize) -> Result, ShellError> { + if let Some(value) = self.nth(pos) { + FromValue::from_value(&value).map(Some) + } else { + Ok(None) + } + } + + pub fn req(&self, pos: usize) -> Result { + if let Some(value) = self.nth(pos) { + FromValue::from_value(&value) + } else { + Err(ShellError::AccessBeyondEnd( + self.positional.len(), + self.head, + )) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use nu_protocol::{Span, Spanned, Value}; + + #[test] + fn call_to_value() { + let call = EvaluatedCall { + head: Span { start: 0, end: 10 }, + positional: vec![ + Value::Float { + val: 1.0, + span: Span { start: 0, end: 10 }, + }, + Value::String { + val: "something".into(), + span: Span { start: 0, end: 10 }, + }, + ], + named: vec![ + ( + Spanned { + item: "name".to_string(), + span: Span { start: 0, end: 10 }, + }, + Some(Value::Float { + val: 1.0, + span: Span { start: 0, end: 10 }, + }), + ), + ( + Spanned { + item: "flag".to_string(), + span: Span { start: 0, end: 10 }, + }, + None, + ), + ], + }; + + let name: Option = call.get_flag("name").unwrap(); + assert_eq!(name, Some(1.0)); + + assert!(call.has_flag("flag")); + + let required: f64 = call.req(0).unwrap(); + assert_eq!(required, 1.0); + + let optional: Option = call.opt(1).unwrap(); + assert_eq!(optional, Some("something".to_string())); + + let rest: Vec = call.rest(1).unwrap(); + assert_eq!(rest, vec!["something".to_string()]); + } +} diff --git a/crates/nu-plugin/src/lib.rs b/crates/nu-plugin/src/lib.rs index ff33a46384..e9af55d453 100644 --- a/crates/nu-plugin/src/lib.rs +++ b/crates/nu-plugin/src/lib.rs @@ -1,6 +1,8 @@ +pub mod evaluated_call; pub mod plugin; pub mod plugin_call; pub mod plugin_capnp; pub mod serializers; +pub use evaluated_call::EvaluatedCall; pub use plugin::{serve_plugin, Plugin}; diff --git a/crates/nu-plugin/src/plugin.rs b/crates/nu-plugin/src/plugin.rs index d6d44d2ab6..9ff85de91a 100644 --- a/crates/nu-plugin/src/plugin.rs +++ b/crates/nu-plugin/src/plugin.rs @@ -1,18 +1,20 @@ use crate::plugin_call::{self, decode_call, encode_response}; use std::io::BufReader; +use std::path::{Path, PathBuf}; use std::process::{Command as CommandSys, Stdio}; -use std::{fmt::Display, path::Path}; -use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet}; use nu_protocol::{ast::Call, Signature, Value}; use nu_protocol::{PipelineData, ShellError}; +use super::evaluated_call::EvaluatedCall; + const OUTPUT_BUFFER_SIZE: usize = 8192; #[derive(Debug)] pub struct CallInfo { pub name: String, - pub call: Call, + pub call: EvaluatedCall, pub input: Value, } @@ -31,43 +33,12 @@ pub enum PluginResponse { Value(Box), } -#[derive(Debug)] -pub enum PluginError { - MissingSignature, - UnableToGetStdout, - UnableToSpawn(String), - EncodingError(String), - DecodingError(String), - RunTimeError(String), -} - -impl Display for PluginError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - PluginError::MissingSignature => write!(f, "missing signature in plugin"), - PluginError::UnableToGetStdout => write!(f, "couldn't get stdout from child process"), - PluginError::UnableToSpawn(err) => { - write!(f, "error in spawned child process: {}", err) - } - PluginError::EncodingError(err) => { - write!(f, "error while encoding: {}", err) - } - PluginError::DecodingError(err) => { - write!(f, "error while decoding: {}", err) - } - PluginError::RunTimeError(err) => { - write!(f, "runtime error: {}", err) - } - } - } -} - -pub fn get_signature(path: &Path) -> Result, PluginError> { +pub fn get_signature(path: &Path) -> Result, ShellError> { let mut plugin_cmd = create_command(path); - let mut child = plugin_cmd - .spawn() - .map_err(|err| PluginError::UnableToSpawn(format!("{}", err)))?; + let mut child = plugin_cmd.spawn().map_err(|err| { + ShellError::InternalError(format!("Error spawning child process: {}", err)) + })?; // Create message to plugin to indicate that signature is required and // send call to plugin asking for signature @@ -84,11 +55,16 @@ pub fn get_signature(path: &Path) -> Result, PluginError> { match response { PluginResponse::Signature(sign) => Ok(sign), - PluginResponse::Error(msg) => Err(PluginError::DecodingError(msg)), - _ => Err(PluginError::DecodingError("signature not found".into())), + PluginResponse::Error(msg) => Err(ShellError::InternalError(format!( + "Plugin response error {}", + msg, + ))), + _ => Err(ShellError::InternalError("Plugin missing signature".into())), } } else { - Err(PluginError::UnableToGetStdout) + Err(ShellError::InternalError( + "Plugin missing stdout reader".into(), + )) }?; // There is no need to wait for the child process to finish since the @@ -120,11 +96,11 @@ fn create_command(path: &Path) -> CommandSys { pub struct PluginDeclaration { name: String, signature: Signature, - filename: String, + filename: PathBuf, } impl PluginDeclaration { - pub fn new(filename: String, signature: Signature) -> Self { + pub fn new(filename: PathBuf, signature: Signature) -> Self { Self { name: signature.name.clone(), signature, @@ -148,8 +124,8 @@ impl Command for PluginDeclaration { fn run( &self, - _engine_state: &EngineState, - _stack: &mut Stack, + engine_state: &EngineState, + stack: &mut Stack, call: &Call, input: PipelineData, ) -> Result { @@ -159,9 +135,14 @@ impl Command for PluginDeclaration { let source_file = Path::new(&self.filename); let mut plugin_cmd = create_command(source_file); - let mut child = plugin_cmd - .spawn() - .map_err(|err| ShellError::PluginError(format!("{}", err)))?; + let mut child = plugin_cmd.spawn().map_err(|err| { + let decl = engine_state.get_decl(call.decl_id); + ShellError::LabeledError( + format!("Unable to spawn plugin for {}", decl.name()), + format!("{}", err), + call.head, + ) + })?; let input = match input { PipelineData::Value(value) => value, @@ -181,49 +162,71 @@ impl Command for PluginDeclaration { // PluginCall information let plugin_call = PluginCall::CallInfo(Box::new(CallInfo { name: self.name.clone(), - call: call.clone(), + call: EvaluatedCall::try_from_call(call, engine_state, stack)?, input, })); let mut writer = stdin_writer; - plugin_call::encode_call(&plugin_call, &mut writer) - .map_err(|err| ShellError::PluginError(err.to_string()))?; + plugin_call::encode_call(&plugin_call, &mut writer).map_err(|err| { + let decl = engine_state.get_decl(call.decl_id); + ShellError::LabeledError( + format!("Unable to encode call for {}", decl.name()), + err.to_string(), + call.head, + ) + })?; } // Deserialize response from plugin to extract the resulting value let pipeline_data = if let Some(stdout_reader) = &mut child.stdout { let reader = stdout_reader; let mut buf_read = BufReader::with_capacity(OUTPUT_BUFFER_SIZE, reader); - let response = plugin_call::decode_response(&mut buf_read) - .map_err(|err| ShellError::PluginError(err.to_string()))?; + let response = plugin_call::decode_response(&mut buf_read).map_err(|err| { + let decl = engine_state.get_decl(call.decl_id); + ShellError::LabeledError( + format!("Unable to decode call for {}", decl.name()), + err.to_string(), + call.head, + ) + })?; match response { PluginResponse::Value(value) => Ok(PipelineData::Value(value.as_ref().clone())), - PluginResponse::Error(msg) => Err(PluginError::DecodingError(msg)), - _ => Err(PluginError::DecodingError( - "result value from plugin not found".into(), + PluginResponse::Error(msg) => Err(ShellError::LabeledError( + "Error received from plugin".into(), + msg, + call.head, + )), + _ => Err(ShellError::LabeledError( + "Plugin missing value".into(), + "No value received from plugin".into(), + call.head, )), } } else { - Err(PluginError::UnableToGetStdout) - } - .map_err(|err| ShellError::PluginError(err.to_string()))?; + Err(ShellError::LabeledError( + "Error with stdout reader".into(), + "no stdout reader".into(), + call.head, + )) + }?; // There is no need to wait for the child process to finish // The response has been collected from the plugin call Ok(pipeline_data) } - fn is_plugin(&self) -> Option<&str> { - Some(self.filename.as_str()) + fn is_plugin(&self) -> Option<&PathBuf> { + Some(&self.filename) } } /// The `Plugin` trait defines the API which plugins use to "hook" into nushell. pub trait Plugin { fn signature(&self) -> Vec; - fn run(&mut self, name: &str, call: &Call, input: &Value) -> Result; + fn run(&mut self, name: &str, call: &EvaluatedCall, input: &Value) + -> Result; } // Function used in the plugin definition for the communication protocol between @@ -261,3 +264,37 @@ pub fn serve_plugin(plugin: &mut impl Plugin) { } } } + +pub fn eval_plugin_signatures(working_set: &mut StateWorkingSet) -> Result<(), ShellError> { + let decls = working_set + .get_signatures() + .map(|(path, signature)| match signature { + Some(signature) => { + let plugin_decl = PluginDeclaration::new(path.clone(), signature.clone()); + let plugin_decl: Box = Box::new(plugin_decl); + Ok(vec![plugin_decl]) + } + None => match get_signature(path.as_path()) { + Ok(signatures) => Ok(signatures + .into_iter() + .map(|signature| { + let plugin_decl = PluginDeclaration::new(path.clone(), signature); + let plugin_decl: Box = Box::new(plugin_decl); + plugin_decl + }) + .collect::>>()), + Err(err) => Err(ShellError::InternalError(format!("{}", err))), + }, + }) + // Need to collect the vector in order to check the error from getting the signature + .collect::>>, ShellError>>()?; + + let decls = decls + .into_iter() + .flatten() + .collect::>>(); + + working_set.add_plugin_decls(decls); + + Ok(()) +} diff --git a/crates/nu-plugin/src/plugin_call.rs b/crates/nu-plugin/src/plugin_call.rs index 4feb8a9283..fb692d6bc9 100644 --- a/crates/nu-plugin/src/plugin_call.rs +++ b/crates/nu-plugin/src/plugin_call.rs @@ -1,14 +1,14 @@ -use crate::plugin::{CallInfo, PluginCall, PluginError, PluginResponse}; +use crate::plugin::{CallInfo, PluginCall, PluginResponse}; use crate::plugin_capnp::{plugin_call, plugin_response}; use crate::serializers::signature::deserialize_signature; use crate::serializers::{call, signature, value}; use capnp::serialize; -use nu_protocol::Signature; +use nu_protocol::{ShellError, Signature}; pub fn encode_call( plugin_call: &PluginCall, writer: &mut impl std::io::Write, -) -> Result<(), PluginError> { +) -> Result<(), ShellError> { let mut message = ::capnp::message::Builder::new_default(); let mut builder = message.init_root::(); @@ -25,56 +25,55 @@ pub fn encode_call( let call_builder = call_info_builder .reborrow() .get_call() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; call::serialize_call(&call_info.call, call_builder) - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; // Serializing the input value from the call info let value_builder = call_info_builder .reborrow() .get_input() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; value::serialize_value(&call_info.input, value_builder); } }; - serialize::write_message(writer, &message) - .map_err(|e| PluginError::EncodingError(e.to_string())) + serialize::write_message(writer, &message).map_err(|e| ShellError::InternalError(e.to_string())) } -pub fn decode_call(reader: &mut impl std::io::BufRead) -> Result { +pub fn decode_call(reader: &mut impl std::io::BufRead) -> Result { let message_reader = serialize::read_message(reader, ::capnp::message::ReaderOptions::new()) - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let reader = message_reader .get_root::() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; match reader.which() { - Err(capnp::NotInSchema(_)) => Err(PluginError::DecodingError("value not in schema".into())), + Err(capnp::NotInSchema(_)) => Err(ShellError::InternalError("value not in schema".into())), Ok(plugin_call::Signature(())) => Ok(PluginCall::Signature), Ok(plugin_call::CallInfo(reader)) => { - let reader = reader.map_err(|e| PluginError::DecodingError(e.to_string()))?; + let reader = reader.map_err(|e| ShellError::InternalError(e.to_string()))?; let name = reader .get_name() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let call_reader = reader .get_call() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let call = call::deserialize_call(call_reader) - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let input_reader = reader .get_input() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let input = value::deserialize_value(input_reader) - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; Ok(PluginCall::CallInfo(Box::new(CallInfo { name: name.to_string(), @@ -88,7 +87,7 @@ pub fn decode_call(reader: &mut impl std::io::BufRead) -> Result Result<(), PluginError> { +) -> Result<(), ShellError> { let mut message = ::capnp::message::Builder::new_default(); let mut builder = message.init_root::(); @@ -110,39 +109,38 @@ pub fn encode_response( } }; - serialize::write_message(writer, &message) - .map_err(|e| PluginError::EncodingError(e.to_string())) + serialize::write_message(writer, &message).map_err(|e| ShellError::InternalError(e.to_string())) } -pub fn decode_response(reader: &mut impl std::io::BufRead) -> Result { +pub fn decode_response(reader: &mut impl std::io::BufRead) -> Result { let message_reader = serialize::read_message(reader, ::capnp::message::ReaderOptions::new()) - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let reader = message_reader .get_root::() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; match reader.which() { - Err(capnp::NotInSchema(_)) => Err(PluginError::DecodingError("value not in schema".into())), + Err(capnp::NotInSchema(_)) => Err(ShellError::InternalError("value not in schema".into())), Ok(plugin_response::Error(reader)) => { - let msg = reader.map_err(|e| PluginError::DecodingError(e.to_string()))?; + let msg = reader.map_err(|e| ShellError::InternalError(e.to_string()))?; Ok(PluginResponse::Error(msg.to_string())) } Ok(plugin_response::Signature(reader)) => { - let reader = reader.map_err(|e| PluginError::DecodingError(e.to_string()))?; + let reader = reader.map_err(|e| ShellError::InternalError(e.to_string()))?; let signatures = reader .iter() .map(deserialize_signature) - .collect::, PluginError>>()?; + .collect::, ShellError>>()?; Ok(PluginResponse::Signature(signatures)) } Ok(plugin_response::Value(reader)) => { - let reader = reader.map_err(|e| PluginError::DecodingError(e.to_string()))?; + let reader = reader.map_err(|e| ShellError::InternalError(e.to_string()))?; let val = value::deserialize_value(reader) - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; Ok(PluginResponse::Value(Box::new(val))) } @@ -152,21 +150,9 @@ pub fn decode_response(reader: &mut impl std::io::BufRead) -> Result assert_eq!(a, b), - (Expr::Int(a), Expr::Int(b)) => assert_eq!(a, b), - (Expr::Float(a), Expr::Float(b)) => assert!((a - b).abs() < f64::EPSILON), - (Expr::String(a), Expr::String(b)) => assert_eq!(a, b), - _ => panic!("not matching values"), - } - } + use nu_protocol::{Signature, Span, Spanned, SyntaxShape, Value}; #[test] fn callinfo_round_trip_signature() { @@ -191,21 +177,16 @@ mod tests { span: Span { start: 1, end: 20 }, }; - let call = Call { - decl_id: 1, + let call = EvaluatedCall { head: Span { start: 0, end: 10 }, positional: vec![ - Expression { - expr: Expr::Float(1.0), + Value::Float { + val: 1.0, span: Span { start: 0, end: 10 }, - ty: nu_protocol::Type::Float, - custom_completion: None, }, - Expression { - expr: Expr::String("something".into()), + Value::String { + val: "something".into(), span: Span { start: 0, end: 10 }, - ty: nu_protocol::Type::Float, - custom_completion: None, }, ], named: vec![( @@ -213,11 +194,9 @@ mod tests { item: "name".to_string(), span: Span { start: 0, end: 10 }, }, - Some(Expression { - expr: Expr::Float(1.0), + Some(Value::Float { + val: 1.0, span: Span { start: 0, end: 10 }, - ty: nu_protocol::Type::Float, - custom_completion: None, }), )], }; @@ -243,7 +222,7 @@ mod tests { call.positional .iter() .zip(call_info.call.positional.iter()) - .for_each(|(lhs, rhs)| compare_expressions(lhs, rhs)); + .for_each(|(lhs, rhs)| assert_eq!(lhs, rhs)); call.named .iter() @@ -254,7 +233,7 @@ mod tests { match (&lhs.1, &rhs.1) { (None, None) => {} - (Some(a), Some(b)) => compare_expressions(a, b), + (Some(a), Some(b)) => assert_eq!(a, b), _ => panic!("not matching values"), } }); diff --git a/crates/nu-plugin/src/plugin_capnp.rs b/crates/nu-plugin/src/plugin_capnp.rs index 2335881511..b1b9c45b01 100644 --- a/crates/nu-plugin/src/plugin_capnp.rs +++ b/crates/nu-plugin/src/plugin_capnp.rs @@ -1171,7 +1171,7 @@ pub mod span { } pub mod value { - pub use self::Which::{Bool, Float, Int, List, String, Void}; + pub use self::Which::{Bool, Float, Int, List, Record, String, Void}; #[derive(Copy, Clone)] pub struct Owned(()); @@ -1258,6 +1258,12 @@ pub mod value { } !self.reader.get_pointer_field(1).is_null() } + pub fn has_record(&self) -> bool { + if self.reader.get_data_field::(0) != 6 { + return false; + } + !self.reader.get_pointer_field(1).is_null() + } #[inline] pub fn which(self) -> ::core::result::Result, ::capnp::NotInSchema> { match self.reader.get_data_field::(0) { @@ -1277,6 +1283,12 @@ pub mod value { ::core::option::Option::None, ), )), + 6 => ::core::result::Result::Ok(Record( + ::capnp::traits::FromPointerReader::get_from_pointer( + &self.reader.get_pointer_field(1), + ::core::option::Option::None, + ), + )), x => ::core::result::Result::Err(::capnp::NotInSchema(x)), } } @@ -1441,6 +1453,29 @@ pub mod value { !self.builder.get_pointer_field(1).is_null() } #[inline] + pub fn set_record( + &mut self, + value: crate::plugin_capnp::record::Reader<'_>, + ) -> ::capnp::Result<()> { + self.builder.set_data_field::(0, 6); + ::capnp::traits::SetPointerBuilder::set_pointer_builder( + self.builder.get_pointer_field(1), + value, + false, + ) + } + #[inline] + pub fn init_record(self) -> crate::plugin_capnp::record::Builder<'a> { + self.builder.set_data_field::(0, 6); + ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(1), 0) + } + pub fn has_record(&self) -> bool { + if self.builder.get_data_field::(0) != 6 { + return false; + } + !self.builder.get_pointer_field(1).is_null() + } + #[inline] pub fn which(self) -> ::core::result::Result, ::capnp::NotInSchema> { match self.builder.get_data_field::(0) { 0 => ::core::result::Result::Ok(Void(())), @@ -1459,6 +1494,12 @@ pub mod value { ::core::option::Option::None, ), )), + 6 => ::core::result::Result::Ok(Record( + ::capnp::traits::FromPointerBuilder::get_from_pointer( + self.builder.get_pointer_field(1), + ::core::option::Option::None, + ), + )), x => ::core::result::Result::Err(::capnp::NotInSchema(x)), } } @@ -1487,24 +1528,267 @@ pub mod value { }; pub const TYPE_ID: u64 = 0x92a0_59fb_5627_86a8; } - pub enum Which { + pub enum Which { Void(()), Bool(bool), Int(i64), Float(f64), String(A0), List(A1), + Record(A2), } pub type WhichReader<'a> = Which< ::capnp::Result<::capnp::text::Reader<'a>>, ::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::value::Owned>>, + ::capnp::Result>, >; pub type WhichBuilder<'a> = Which< ::capnp::Result<::capnp::text::Builder<'a>>, ::capnp::Result<::capnp::struct_list::Builder<'a, crate::plugin_capnp::value::Owned>>, + ::capnp::Result>, >; } +pub mod record { + #[derive(Copy, Clone)] + pub struct Owned(()); + impl<'a> ::capnp::traits::Owned<'a> for Owned { + type Reader = Reader<'a>; + type Builder = Builder<'a>; + } + impl<'a> ::capnp::traits::OwnedStruct<'a> for Owned { + type Reader = Reader<'a>; + type Builder = Builder<'a>; + } + impl ::capnp::traits::Pipelined for Owned { + type Pipeline = Pipeline; + } + + #[derive(Clone, Copy)] + pub struct Reader<'a> { + reader: ::capnp::private::layout::StructReader<'a>, + } + + impl<'a> ::capnp::traits::HasTypeId for Reader<'a> { + #[inline] + fn type_id() -> u64 { + _private::TYPE_ID + } + } + impl<'a> ::capnp::traits::FromStructReader<'a> for Reader<'a> { + fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a> { + Reader { reader } + } + } + + impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> { + fn get_from_pointer( + reader: &::capnp::private::layout::PointerReader<'a>, + default: ::core::option::Option<&'a [capnp::Word]>, + ) -> ::capnp::Result> { + ::core::result::Result::Ok(::capnp::traits::FromStructReader::new( + reader.get_struct(default)?, + )) + } + } + + impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> { + fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> { + self.reader + } + } + + impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> { + fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) { + self.reader + .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table)) + } + } + + impl<'a> Reader<'a> { + pub fn reborrow(&self) -> Reader<'_> { + Reader { ..*self } + } + + pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> { + self.reader.total_size() + } + #[inline] + pub fn get_cols(self) -> ::capnp::Result<::capnp::text_list::Reader<'a>> { + ::capnp::traits::FromPointerReader::get_from_pointer( + &self.reader.get_pointer_field(0), + ::core::option::Option::None, + ) + } + pub fn has_cols(&self) -> bool { + !self.reader.get_pointer_field(0).is_null() + } + #[inline] + pub fn get_vals( + self, + ) -> ::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::value::Owned>> + { + ::capnp::traits::FromPointerReader::get_from_pointer( + &self.reader.get_pointer_field(1), + ::core::option::Option::None, + ) + } + pub fn has_vals(&self) -> bool { + !self.reader.get_pointer_field(1).is_null() + } + } + + pub struct Builder<'a> { + builder: ::capnp::private::layout::StructBuilder<'a>, + } + impl<'a> ::capnp::traits::HasStructSize for Builder<'a> { + #[inline] + fn struct_size() -> ::capnp::private::layout::StructSize { + _private::STRUCT_SIZE + } + } + impl<'a> ::capnp::traits::HasTypeId for Builder<'a> { + #[inline] + fn type_id() -> u64 { + _private::TYPE_ID + } + } + impl<'a> ::capnp::traits::FromStructBuilder<'a> for Builder<'a> { + fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a> { + Builder { builder } + } + } + + impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> { + fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) { + self.builder + .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table)) + } + } + + impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> { + fn init_pointer( + builder: ::capnp::private::layout::PointerBuilder<'a>, + _size: u32, + ) -> Builder<'a> { + ::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE)) + } + fn get_from_pointer( + builder: ::capnp::private::layout::PointerBuilder<'a>, + default: ::core::option::Option<&'a [capnp::Word]>, + ) -> ::capnp::Result> { + ::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new( + builder.get_struct(_private::STRUCT_SIZE, default)?, + )) + } + } + + impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> { + fn set_pointer_builder<'b>( + pointer: ::capnp::private::layout::PointerBuilder<'b>, + value: Reader<'a>, + canonicalize: bool, + ) -> ::capnp::Result<()> { + pointer.set_struct(&value.reader, canonicalize) + } + } + + impl<'a> Builder<'a> { + pub fn into_reader(self) -> Reader<'a> { + ::capnp::traits::FromStructReader::new(self.builder.into_reader()) + } + pub fn reborrow(&mut self) -> Builder<'_> { + Builder { ..*self } + } + pub fn reborrow_as_reader(&self) -> Reader<'_> { + ::capnp::traits::FromStructReader::new(self.builder.into_reader()) + } + + pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> { + self.builder.into_reader().total_size() + } + #[inline] + pub fn get_cols(self) -> ::capnp::Result<::capnp::text_list::Builder<'a>> { + ::capnp::traits::FromPointerBuilder::get_from_pointer( + self.builder.get_pointer_field(0), + ::core::option::Option::None, + ) + } + #[inline] + pub fn set_cols(&mut self, value: ::capnp::text_list::Reader<'a>) -> ::capnp::Result<()> { + ::capnp::traits::SetPointerBuilder::set_pointer_builder( + self.builder.get_pointer_field(0), + value, + false, + ) + } + #[inline] + pub fn init_cols(self, size: u32) -> ::capnp::text_list::Builder<'a> { + ::capnp::traits::FromPointerBuilder::init_pointer( + self.builder.get_pointer_field(0), + size, + ) + } + pub fn has_cols(&self) -> bool { + !self.builder.get_pointer_field(0).is_null() + } + #[inline] + pub fn get_vals( + self, + ) -> ::capnp::Result<::capnp::struct_list::Builder<'a, crate::plugin_capnp::value::Owned>> + { + ::capnp::traits::FromPointerBuilder::get_from_pointer( + self.builder.get_pointer_field(1), + ::core::option::Option::None, + ) + } + #[inline] + pub fn set_vals( + &mut self, + value: ::capnp::struct_list::Reader<'a, crate::plugin_capnp::value::Owned>, + ) -> ::capnp::Result<()> { + ::capnp::traits::SetPointerBuilder::set_pointer_builder( + self.builder.get_pointer_field(1), + value, + false, + ) + } + #[inline] + pub fn init_vals( + self, + size: u32, + ) -> ::capnp::struct_list::Builder<'a, crate::plugin_capnp::value::Owned> { + ::capnp::traits::FromPointerBuilder::init_pointer( + self.builder.get_pointer_field(1), + size, + ) + } + pub fn has_vals(&self) -> bool { + !self.builder.get_pointer_field(1).is_null() + } + } + + pub struct Pipeline { + _typeless: ::capnp::any_pointer::Pipeline, + } + impl ::capnp::capability::FromTypelessPipeline for Pipeline { + fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline { + Pipeline { + _typeless: typeless, + } + } + } + impl Pipeline {} + mod _private { + use capnp::private::layout; + pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { + data: 0, + pointers: 2, + }; + pub const TYPE_ID: u64 = 0xd435_7cbb_f79b_12fb; + } +} + pub mod signature { #[derive(Copy, Clone)] pub struct Owned(()); @@ -2543,303 +2827,7 @@ impl ::capnp::traits::HasTypeId for Shape { } } -pub mod expression { - pub use self::Which::{Bool, Float, Garbage, Int, List, String}; - - #[derive(Copy, Clone)] - pub struct Owned(()); - impl<'a> ::capnp::traits::Owned<'a> for Owned { - type Reader = Reader<'a>; - type Builder = Builder<'a>; - } - impl<'a> ::capnp::traits::OwnedStruct<'a> for Owned { - type Reader = Reader<'a>; - type Builder = Builder<'a>; - } - impl ::capnp::traits::Pipelined for Owned { - type Pipeline = Pipeline; - } - - #[derive(Clone, Copy)] - pub struct Reader<'a> { - reader: ::capnp::private::layout::StructReader<'a>, - } - - impl<'a> ::capnp::traits::HasTypeId for Reader<'a> { - #[inline] - fn type_id() -> u64 { - _private::TYPE_ID - } - } - impl<'a> ::capnp::traits::FromStructReader<'a> for Reader<'a> { - fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a> { - Reader { reader } - } - } - - impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> { - fn get_from_pointer( - reader: &::capnp::private::layout::PointerReader<'a>, - default: ::core::option::Option<&'a [capnp::Word]>, - ) -> ::capnp::Result> { - ::core::result::Result::Ok(::capnp::traits::FromStructReader::new( - reader.get_struct(default)?, - )) - } - } - - impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> { - fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> { - self.reader - } - } - - impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> { - fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) { - self.reader - .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table)) - } - } - - impl<'a> Reader<'a> { - pub fn reborrow(&self) -> Reader<'_> { - Reader { ..*self } - } - - pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> { - self.reader.total_size() - } - pub fn has_string(&self) -> bool { - if self.reader.get_data_field::(0) != 4 { - return false; - } - !self.reader.get_pointer_field(0).is_null() - } - pub fn has_list(&self) -> bool { - if self.reader.get_data_field::(0) != 5 { - return false; - } - !self.reader.get_pointer_field(0).is_null() - } - #[inline] - pub fn which(self) -> ::core::result::Result, ::capnp::NotInSchema> { - match self.reader.get_data_field::(0) { - 0 => ::core::result::Result::Ok(Garbage(())), - 1 => ::core::result::Result::Ok(Bool(self.reader.get_bool_field(16))), - 2 => ::core::result::Result::Ok(Int(self.reader.get_data_field::(1))), - 3 => ::core::result::Result::Ok(Float(self.reader.get_data_field::(1))), - 4 => ::core::result::Result::Ok(String( - ::capnp::traits::FromPointerReader::get_from_pointer( - &self.reader.get_pointer_field(0), - ::core::option::Option::None, - ), - )), - 5 => ::core::result::Result::Ok(List( - ::capnp::traits::FromPointerReader::get_from_pointer( - &self.reader.get_pointer_field(0), - ::core::option::Option::None, - ), - )), - x => ::core::result::Result::Err(::capnp::NotInSchema(x)), - } - } - } - - pub struct Builder<'a> { - builder: ::capnp::private::layout::StructBuilder<'a>, - } - impl<'a> ::capnp::traits::HasStructSize for Builder<'a> { - #[inline] - fn struct_size() -> ::capnp::private::layout::StructSize { - _private::STRUCT_SIZE - } - } - impl<'a> ::capnp::traits::HasTypeId for Builder<'a> { - #[inline] - fn type_id() -> u64 { - _private::TYPE_ID - } - } - impl<'a> ::capnp::traits::FromStructBuilder<'a> for Builder<'a> { - fn new(builder: ::capnp::private::layout::StructBuilder<'a>) -> Builder<'a> { - Builder { builder } - } - } - - impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> { - fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) { - self.builder - .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table)) - } - } - - impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> { - fn init_pointer( - builder: ::capnp::private::layout::PointerBuilder<'a>, - _size: u32, - ) -> Builder<'a> { - ::capnp::traits::FromStructBuilder::new(builder.init_struct(_private::STRUCT_SIZE)) - } - fn get_from_pointer( - builder: ::capnp::private::layout::PointerBuilder<'a>, - default: ::core::option::Option<&'a [capnp::Word]>, - ) -> ::capnp::Result> { - ::core::result::Result::Ok(::capnp::traits::FromStructBuilder::new( - builder.get_struct(_private::STRUCT_SIZE, default)?, - )) - } - } - - impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> { - fn set_pointer_builder<'b>( - pointer: ::capnp::private::layout::PointerBuilder<'b>, - value: Reader<'a>, - canonicalize: bool, - ) -> ::capnp::Result<()> { - pointer.set_struct(&value.reader, canonicalize) - } - } - - impl<'a> Builder<'a> { - pub fn into_reader(self) -> Reader<'a> { - ::capnp::traits::FromStructReader::new(self.builder.into_reader()) - } - pub fn reborrow(&mut self) -> Builder<'_> { - Builder { ..*self } - } - pub fn reborrow_as_reader(&self) -> Reader<'_> { - ::capnp::traits::FromStructReader::new(self.builder.into_reader()) - } - - pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> { - self.builder.into_reader().total_size() - } - #[inline] - pub fn set_garbage(&mut self, _value: ()) { - self.builder.set_data_field::(0, 0); - } - #[inline] - pub fn set_bool(&mut self, value: bool) { - self.builder.set_data_field::(0, 1); - self.builder.set_bool_field(16, value); - } - #[inline] - pub fn set_int(&mut self, value: i64) { - self.builder.set_data_field::(0, 2); - self.builder.set_data_field::(1, value); - } - #[inline] - pub fn set_float(&mut self, value: f64) { - self.builder.set_data_field::(0, 3); - self.builder.set_data_field::(1, value); - } - #[inline] - pub fn set_string(&mut self, value: ::capnp::text::Reader<'_>) { - self.builder.set_data_field::(0, 4); - self.builder.get_pointer_field(0).set_text(value); - } - #[inline] - pub fn init_string(self, size: u32) -> ::capnp::text::Builder<'a> { - self.builder.set_data_field::(0, 4); - self.builder.get_pointer_field(0).init_text(size) - } - pub fn has_string(&self) -> bool { - if self.builder.get_data_field::(0) != 4 { - return false; - } - !self.builder.get_pointer_field(0).is_null() - } - #[inline] - pub fn set_list( - &mut self, - value: ::capnp::struct_list::Reader<'a, crate::plugin_capnp::expression::Owned>, - ) -> ::capnp::Result<()> { - self.builder.set_data_field::(0, 5); - ::capnp::traits::SetPointerBuilder::set_pointer_builder( - self.builder.get_pointer_field(0), - value, - false, - ) - } - #[inline] - pub fn init_list( - self, - size: u32, - ) -> ::capnp::struct_list::Builder<'a, crate::plugin_capnp::expression::Owned> { - self.builder.set_data_field::(0, 5); - ::capnp::traits::FromPointerBuilder::init_pointer( - self.builder.get_pointer_field(0), - size, - ) - } - pub fn has_list(&self) -> bool { - if self.builder.get_data_field::(0) != 5 { - return false; - } - !self.builder.get_pointer_field(0).is_null() - } - #[inline] - pub fn which(self) -> ::core::result::Result, ::capnp::NotInSchema> { - match self.builder.get_data_field::(0) { - 0 => ::core::result::Result::Ok(Garbage(())), - 1 => ::core::result::Result::Ok(Bool(self.builder.get_bool_field(16))), - 2 => ::core::result::Result::Ok(Int(self.builder.get_data_field::(1))), - 3 => ::core::result::Result::Ok(Float(self.builder.get_data_field::(1))), - 4 => ::core::result::Result::Ok(String( - ::capnp::traits::FromPointerBuilder::get_from_pointer( - self.builder.get_pointer_field(0), - ::core::option::Option::None, - ), - )), - 5 => ::core::result::Result::Ok(List( - ::capnp::traits::FromPointerBuilder::get_from_pointer( - self.builder.get_pointer_field(0), - ::core::option::Option::None, - ), - )), - x => ::core::result::Result::Err(::capnp::NotInSchema(x)), - } - } - } - - pub struct Pipeline { - _typeless: ::capnp::any_pointer::Pipeline, - } - impl ::capnp::capability::FromTypelessPipeline for Pipeline { - fn new(typeless: ::capnp::any_pointer::Pipeline) -> Pipeline { - Pipeline { - _typeless: typeless, - } - } - } - impl Pipeline {} - mod _private { - use capnp::private::layout; - pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { - data: 2, - pointers: 1, - }; - pub const TYPE_ID: u64 = 0xb831_c4c2_80ed_4dbb; - } - pub enum Which { - Garbage(()), - Bool(bool), - Int(i64), - Float(f64), - String(A0), - List(A1), - } - pub type WhichReader<'a> = Which< - ::capnp::Result<::capnp::text::Reader<'a>>, - ::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::expression::Owned>>, - >; - pub type WhichBuilder<'a> = Which< - ::capnp::Result<::capnp::text::Builder<'a>>, - ::capnp::Result<::capnp::struct_list::Builder<'a, crate::plugin_capnp::expression::Owned>>, - >; -} - -pub mod call { +pub mod evaluated_call { #[derive(Copy, Clone)] pub struct Owned(()); impl<'a> ::capnp::traits::Owned<'a> for Owned { @@ -2916,7 +2904,7 @@ pub mod call { #[inline] pub fn get_positional( self, - ) -> ::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::expression::Owned>> + ) -> ::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::value::Owned>> { ::capnp::traits::FromPointerReader::get_from_pointer( &self.reader.get_pointer_field(1), @@ -2933,7 +2921,7 @@ pub mod call { crate::plugin_capnp::map::Reader< 'a, ::capnp::text::Owned, - crate::plugin_capnp::expression::Owned, + crate::plugin_capnp::value::Owned, >, > { ::capnp::traits::FromPointerReader::get_from_pointer( @@ -3043,9 +3031,8 @@ pub mod call { #[inline] pub fn get_positional( self, - ) -> ::capnp::Result< - ::capnp::struct_list::Builder<'a, crate::plugin_capnp::expression::Owned>, - > { + ) -> ::capnp::Result<::capnp::struct_list::Builder<'a, crate::plugin_capnp::value::Owned>> + { ::capnp::traits::FromPointerBuilder::get_from_pointer( self.builder.get_pointer_field(1), ::core::option::Option::None, @@ -3054,7 +3041,7 @@ pub mod call { #[inline] pub fn set_positional( &mut self, - value: ::capnp::struct_list::Reader<'a, crate::plugin_capnp::expression::Owned>, + value: ::capnp::struct_list::Reader<'a, crate::plugin_capnp::value::Owned>, ) -> ::capnp::Result<()> { ::capnp::traits::SetPointerBuilder::set_pointer_builder( self.builder.get_pointer_field(1), @@ -3066,7 +3053,7 @@ pub mod call { pub fn init_positional( self, size: u32, - ) -> ::capnp::struct_list::Builder<'a, crate::plugin_capnp::expression::Owned> { + ) -> ::capnp::struct_list::Builder<'a, crate::plugin_capnp::value::Owned> { ::capnp::traits::FromPointerBuilder::init_pointer( self.builder.get_pointer_field(1), size, @@ -3082,7 +3069,7 @@ pub mod call { crate::plugin_capnp::map::Builder< 'a, ::capnp::text::Owned, - crate::plugin_capnp::expression::Owned, + crate::plugin_capnp::value::Owned, >, > { ::capnp::traits::FromPointerBuilder::get_from_pointer( @@ -3096,13 +3083,13 @@ pub mod call { value: crate::plugin_capnp::map::Reader< '_, ::capnp::text::Owned, - crate::plugin_capnp::expression::Owned, + crate::plugin_capnp::value::Owned, >, ) -> ::capnp::Result<()> { as ::capnp::traits::SetPointerBuilder>::set_pointer_builder( self.builder.get_pointer_field(2), value, @@ -3115,7 +3102,7 @@ pub mod call { ) -> crate::plugin_capnp::map::Builder< 'a, ::capnp::text::Owned, - crate::plugin_capnp::expression::Owned, + crate::plugin_capnp::value::Owned, > { ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(2), 0) } @@ -3142,7 +3129,7 @@ pub mod call { &self, ) -> crate::plugin_capnp::map::Pipeline< ::capnp::text::Owned, - crate::plugin_capnp::expression::Owned, + crate::plugin_capnp::value::Owned, > { ::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(2)) } @@ -3153,7 +3140,7 @@ pub mod call { data: 0, pointers: 3, }; - pub const TYPE_ID: u64 = 0xf5e6_e69c_460f_37d8; + pub const TYPE_ID: u64 = 0x84fb_ac77_3ee4_48a4; } } @@ -3232,7 +3219,7 @@ pub mod call_info { !self.reader.get_pointer_field(0).is_null() } #[inline] - pub fn get_call(self) -> ::capnp::Result> { + pub fn get_call(self) -> ::capnp::Result> { ::capnp::traits::FromPointerReader::get_from_pointer( &self.reader.get_pointer_field(1), ::core::option::Option::None, @@ -3341,7 +3328,7 @@ pub mod call_info { !self.builder.get_pointer_field(0).is_null() } #[inline] - pub fn get_call(self) -> ::capnp::Result> { + pub fn get_call(self) -> ::capnp::Result> { ::capnp::traits::FromPointerBuilder::get_from_pointer( self.builder.get_pointer_field(1), ::core::option::Option::None, @@ -3350,7 +3337,7 @@ pub mod call_info { #[inline] pub fn set_call( &mut self, - value: crate::plugin_capnp::call::Reader<'_>, + value: crate::plugin_capnp::evaluated_call::Reader<'_>, ) -> ::capnp::Result<()> { ::capnp::traits::SetPointerBuilder::set_pointer_builder( self.builder.get_pointer_field(1), @@ -3359,7 +3346,7 @@ pub mod call_info { ) } #[inline] - pub fn init_call(self) -> crate::plugin_capnp::call::Builder<'a> { + pub fn init_call(self) -> crate::plugin_capnp::evaluated_call::Builder<'a> { ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(1), 0) } pub fn has_call(&self) -> bool { @@ -3403,7 +3390,7 @@ pub mod call_info { } } impl Pipeline { - pub fn get_call(&self) -> crate::plugin_capnp::call::Pipeline { + pub fn get_call(&self) -> crate::plugin_capnp::evaluated_call::Pipeline { ::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(1)) } pub fn get_input(&self) -> crate::plugin_capnp::value::Pipeline { diff --git a/crates/nu-plugin/src/serializers/call.rs b/crates/nu-plugin/src/serializers/call.rs index 55512f1948..7787d05a23 100644 --- a/crates/nu-plugin/src/serializers/call.rs +++ b/crates/nu-plugin/src/serializers/call.rs @@ -1,11 +1,11 @@ -use crate::plugin::PluginError; -use crate::plugin_capnp::{call, expression}; -use nu_protocol::{ - ast::{Call, Expr, Expression}, - Span, Spanned, Type, -}; +use super::value; +use crate::{evaluated_call::EvaluatedCall, plugin_capnp::evaluated_call}; +use nu_protocol::{ShellError, Span, Spanned, Value}; -pub(crate) fn serialize_call(call: &Call, mut builder: call::Builder) -> Result<(), PluginError> { +pub(crate) fn serialize_call( + call: &EvaluatedCall, + mut builder: evaluated_call::Builder, +) -> Result<(), ShellError> { let mut head = builder.reborrow().init_head(); head.set_start(call.head.start as u64); head.set_end(call.head.end as u64); @@ -16,18 +16,18 @@ pub(crate) fn serialize_call(call: &Call, mut builder: call::Builder) -> Result< Ok(()) } -fn serialize_positional(positional: &[Expression], mut builder: call::Builder) { +fn serialize_positional(positional: &[Value], mut builder: evaluated_call::Builder) { let mut positional_builder = builder.reborrow().init_positional(positional.len() as u32); - for (index, expression) in positional.iter().enumerate() { - serialize_expression(expression, positional_builder.reborrow().get(index as u32)) + for (index, value) in positional.iter().enumerate() { + value::serialize_value(value, positional_builder.reborrow().get(index as u32)) } } fn serialize_named( - named: &[(Spanned, Option)], - mut builder: call::Builder, -) -> Result<(), PluginError> { + named: &[(Spanned, Option)], + mut builder: evaluated_call::Builder, +) -> Result<(), ShellError> { let mut named_builder = builder .reborrow() .init_named() @@ -38,42 +38,23 @@ fn serialize_named( entry_builder .reborrow() .set_key(key.item.as_str()) - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; - if let Some(expr) = expression { + if let Some(value) = expression { let value_builder = entry_builder.init_value(); - serialize_expression(expr, value_builder); + value::serialize_value(value, value_builder); } } Ok(()) } -fn serialize_expression(expression: &Expression, mut builder: expression::Builder) { - match &expression.expr { - Expr::Garbage => builder.set_garbage(()), - Expr::Bool(val) => builder.set_bool(*val), - Expr::Int(val) => builder.set_int(*val), - Expr::Float(val) => builder.set_float(*val), - Expr::String(val) => builder.set_string(val), - Expr::List(values) => { - let mut list_builder = builder.reborrow().init_list(values.len() as u32); - for (index, expression) in values.iter().enumerate() { - let inner_builder = list_builder.reborrow().get(index as u32); - serialize_expression(expression, inner_builder) - } - } - _ => { - // If there is the need to pass other type of argument to the plugin - // we have to define the encoding for that parameter in this match - } - } -} - -pub(crate) fn deserialize_call(reader: call::Reader) -> Result { +pub(crate) fn deserialize_call( + reader: evaluated_call::Reader, +) -> Result { let head_reader = reader .get_head() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let head = Span { start: head_reader.get_start() as usize, @@ -83,8 +64,7 @@ pub(crate) fn deserialize_call(reader: call::Reader) -> Result Result Result, PluginError> { + _span: Span, + reader: evaluated_call::Reader, +) -> Result, ShellError> { let positional_reader = reader .get_positional() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; positional_reader .iter() - .map(|expression_reader| deserialize_expression(span, expression_reader)) + .map(value::deserialize_value) .collect() } -type NamedList = Vec<(Spanned, Option)>; +type NamedList = Vec<(Spanned, Option)>; -fn deserialize_named(span: Span, reader: call::Reader) -> Result { +fn deserialize_named(span: Span, reader: evaluated_call::Reader) -> Result { let named_reader = reader .get_named() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let entries_list = named_reader .get_entries() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; - let mut entries: Vec<(Spanned, Option)> = + let mut entries: Vec<(Spanned, Option)> = Vec::with_capacity(entries_list.len() as usize); for entry_reader in entries_list { let item = entry_reader .get_key() - .map_err(|e| PluginError::DecodingError(e.to_string()))? + .map_err(|e| ShellError::InternalError(e.to_string()))? .to_string(); let value = if entry_reader.has_value() { let value_reader = entry_reader .get_value() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; - let expression = deserialize_expression(span, value_reader) - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + let value = value::deserialize_value(value_reader) + .map_err(|e| ShellError::InternalError(e.to_string()))?; - Some(expression) + Some(value) } else { None }; @@ -146,102 +126,50 @@ fn deserialize_named(span: Span, reader: call::Reader) -> Result Result { - let expr = match reader.which() { - Ok(expression::Garbage(())) => Expr::Garbage, - Ok(expression::Bool(val)) => Expr::Bool(val), - Ok(expression::Int(val)) => Expr::Int(val), - Ok(expression::Float(val)) => Expr::Float(val), - Ok(expression::String(val)) => { - let string = val - .map_err(|e| PluginError::DecodingError(e.to_string()))? - .to_string(); - - Expr::String(string) - } - Ok(expression::List(values)) => { - let values = values.map_err(|e| PluginError::DecodingError(e.to_string()))?; - - let values_list = values - .iter() - .map(|inner_reader| deserialize_expression(span, inner_reader)) - .collect::, PluginError>>()?; - - Expr::List(values_list) - } - Err(capnp::NotInSchema(_)) => Expr::Garbage, - }; - - Ok(Expression { - expr, - span, - ty: Type::Unknown, - custom_completion: None, - }) -} - #[cfg(test)] mod tests { use capnp::serialize; use core::panic; use super::*; - use nu_protocol::{ - ast::{Call, Expr, Expression}, - Span, Spanned, - }; + use nu_protocol::{Span, Spanned, Value}; - fn write_buffer(call: &Call, writer: &mut impl std::io::Write) -> Result<(), PluginError> { + fn write_buffer( + call: &EvaluatedCall, + writer: &mut impl std::io::Write, + ) -> Result<(), ShellError> { let mut message = ::capnp::message::Builder::new_default(); - let builder = message.init_root::(); + let builder = message.init_root::(); serialize_call(call, builder)?; serialize::write_message(writer, &message) - .map_err(|e| PluginError::EncodingError(e.to_string())) + .map_err(|e| ShellError::InternalError(e.to_string())) } - fn read_buffer(reader: &mut impl std::io::BufRead) -> Result { + fn read_buffer(reader: &mut impl std::io::BufRead) -> Result { let message_reader = serialize::read_message(reader, ::capnp::message::ReaderOptions::new()).unwrap(); let reader = message_reader - .get_root::() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .get_root::() + .map_err(|e| ShellError::InternalError(e.to_string()))?; deserialize_call(reader) } - fn compare_expressions(lhs: &Expression, rhs: &Expression) { - match (&lhs.expr, &rhs.expr) { - (Expr::Bool(a), Expr::Bool(b)) => assert_eq!(a, b), - (Expr::Int(a), Expr::Int(b)) => assert_eq!(a, b), - (Expr::Float(a), Expr::Float(b)) => assert!((a - b).abs() < f64::EPSILON), - (Expr::String(a), Expr::String(b)) => assert_eq!(a, b), - _ => panic!("not matching values"), - } - } - #[test] fn call_round_trip() { - let call = Call { - decl_id: 1, + let call = EvaluatedCall { head: Span { start: 0, end: 10 }, positional: vec![ - Expression { - expr: Expr::Float(1.0), + Value::Float { + val: 1.0, span: Span { start: 0, end: 10 }, - ty: nu_protocol::Type::Float, - custom_completion: None, }, - Expression { - expr: Expr::String("something".into()), + Value::String { + val: "something".into(), span: Span { start: 0, end: 10 }, - ty: nu_protocol::Type::Float, - custom_completion: None, }, ], named: vec![ @@ -250,11 +178,9 @@ mod tests { item: "name".to_string(), span: Span { start: 0, end: 10 }, }, - Some(Expression { - expr: Expr::Float(1.0), + Some(Value::Float { + val: 1.0, span: Span { start: 0, end: 10 }, - ty: nu_protocol::Type::Float, - custom_completion: None, }), ), ( @@ -277,7 +203,7 @@ mod tests { call.positional .iter() .zip(returned_call.positional.iter()) - .for_each(|(lhs, rhs)| compare_expressions(lhs, rhs)); + .for_each(|(lhs, rhs)| assert_eq!(lhs, rhs)); call.named .iter() @@ -288,7 +214,7 @@ mod tests { match (&lhs.1, &rhs.1) { (None, None) => {} - (Some(a), Some(b)) => compare_expressions(a, b), + (Some(a), Some(b)) => assert_eq!(a, b), _ => panic!("not matching values"), } }); diff --git a/crates/nu-plugin/src/serializers/signature.rs b/crates/nu-plugin/src/serializers/signature.rs index 539e23ca8f..258a528f59 100644 --- a/crates/nu-plugin/src/serializers/signature.rs +++ b/crates/nu-plugin/src/serializers/signature.rs @@ -1,6 +1,5 @@ -use crate::plugin::PluginError; use crate::plugin_capnp::{argument, flag, signature, Category as PluginCategory, Shape}; -use nu_protocol::{Category, Flag, PositionalArg, Signature, SyntaxShape}; +use nu_protocol::{Category, Flag, PositionalArg, ShellError, Signature, SyntaxShape}; pub(crate) fn serialize_signature(signature: &Signature, mut builder: signature::Builder) { builder.set_name(signature.name.as_str()); @@ -46,14 +45,13 @@ pub(crate) fn serialize_signature(signature: &Signature, mut builder: signature: } // Serializing rest argument - let rest_argument = builder.reborrow().init_rest(); if let Some(arg) = &signature.rest_positional { + let rest_argument = builder.reborrow().init_rest(); serialize_argument(arg, rest_argument) } // Serializing the named arguments let mut named_list = builder.reborrow().init_named(signature.named.len() as u32); - for (index, arg) in signature.named.iter().enumerate() { let inner_builder = named_list.reborrow().get(index as u32); serialize_flag(arg, inner_builder) @@ -95,21 +93,21 @@ fn serialize_flag(arg: &Flag, mut builder: flag::Builder) { } } -pub(crate) fn deserialize_signature(reader: signature::Reader) -> Result { +pub(crate) fn deserialize_signature(reader: signature::Reader) -> Result { let name = reader .get_name() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let usage = reader .get_usage() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let extra_usage = reader .get_extra_usage() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let is_filter = reader.get_is_filter(); let category = match reader .get_category() - .map_err(|e| PluginError::EncodingError(e.to_string()))? + .map_err(|e| ShellError::InternalError(e.to_string()))? { PluginCategory::Default => Category::Default, PluginCategory::Conversions => Category::Conversions, @@ -129,28 +127,28 @@ pub(crate) fn deserialize_signature(reader: signature::Reader) -> Result, PluginError>>()?; + .collect::, ShellError>>()?; // Deserializing optional arguments let optional_list = reader .get_optional_positional() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let optional_positional = optional_list .iter() .map(deserialize_argument) - .collect::, PluginError>>()?; + .collect::, ShellError>>()?; // Deserializing rest arguments let rest_positional = if reader.has_rest() { let argument_reader = reader .get_rest() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; Some(deserialize_argument(argument_reader)?) } else { @@ -160,12 +158,12 @@ pub(crate) fn deserialize_signature(reader: signature::Reader) -> Result, PluginError>>()?; + .collect::, ShellError>>()?; Ok(Signature { name: name.to_string(), @@ -181,18 +179,18 @@ pub(crate) fn deserialize_signature(reader: signature::Reader) -> Result Result { +fn deserialize_argument(reader: argument::Reader) -> Result { let name = reader .get_name() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let desc = reader .get_desc() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let shape = reader .get_shape() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let shape = match shape { Shape::String => SyntaxShape::String, @@ -211,21 +209,21 @@ fn deserialize_argument(reader: argument::Reader) -> Result Result { +fn deserialize_flag(reader: flag::Reader) -> Result { let long = reader .get_long() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let desc = reader .get_desc() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let required = reader.get_required(); let short = if reader.has_short() { let short_reader = reader .get_short() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; short_reader.chars().next() } else { @@ -234,7 +232,7 @@ fn deserialize_flag(reader: flag::Reader) -> Result { let arg = reader .get_arg() - .map_err(|e| PluginError::EncodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let arg = match arg { Shape::None => None, @@ -264,7 +262,7 @@ mod tests { pub fn write_buffer( signature: &Signature, writer: &mut impl std::io::Write, - ) -> Result<(), PluginError> { + ) -> Result<(), ShellError> { let mut message = ::capnp::message::Builder::new_default(); let builder = message.init_root::(); @@ -272,16 +270,16 @@ mod tests { serialize_signature(signature, builder); serialize::write_message(writer, &message) - .map_err(|e| PluginError::EncodingError(e.to_string())) + .map_err(|e| ShellError::InternalError(e.to_string())) } - pub fn read_buffer(reader: &mut impl std::io::BufRead) -> Result { + pub fn read_buffer(reader: &mut impl std::io::BufRead) -> Result { let message_reader = serialize::read_message(reader, ::capnp::message::ReaderOptions::new()).unwrap(); let reader = message_reader .get_root::() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; deserialize_signature(reader) } @@ -292,12 +290,9 @@ mod tests { .required("first", SyntaxShape::String, "first required") .required("second", SyntaxShape::Int, "second required") .required_named("first_named", SyntaxShape::String, "first named", Some('f')) - .required_named( - "second_named", - SyntaxShape::String, - "second named", - Some('s'), - ) + .required_named("second_named", SyntaxShape::Int, "first named", Some('s')) + .required_named("name", SyntaxShape::String, "first named", Some('n')) + .required_named("string", SyntaxShape::String, "second named", Some('x')) .switch("switch", "some switch", None) .rest("remaining", SyntaxShape::Int, "remaining") .category(Category::Conversions); @@ -336,4 +331,50 @@ mod tests { returned_signature.rest_positional, ); } + + #[test] + fn value_round_trip_2() { + let signature = Signature::build("test-1") + .desc("Signature test 1 for plugin. Returns Value::Nothing") + .required("a", SyntaxShape::Int, "required integer value") + .required("b", SyntaxShape::String, "required string value") + .optional("opt", SyntaxShape::Boolean, "Optional boolean") + .switch("flag", "a flag for the signature", Some('f')) + .named("named", SyntaxShape::String, "named string", Some('n')) + .category(Category::Experimental); + + let mut buffer: Vec = Vec::new(); + write_buffer(&signature, &mut buffer).expect("unable to serialize message"); + let returned_signature = + read_buffer(&mut buffer.as_slice()).expect("unable to deserialize message"); + + assert_eq!(signature.name, returned_signature.name); + assert_eq!(signature.usage, returned_signature.usage); + assert_eq!(signature.extra_usage, returned_signature.extra_usage); + assert_eq!(signature.is_filter, returned_signature.is_filter); + assert_eq!(signature.category, returned_signature.category); + + signature + .required_positional + .iter() + .zip(returned_signature.required_positional.iter()) + .for_each(|(lhs, rhs)| assert_eq!(lhs, rhs)); + + signature + .optional_positional + .iter() + .zip(returned_signature.optional_positional.iter()) + .for_each(|(lhs, rhs)| assert_eq!(lhs, rhs)); + + signature + .named + .iter() + .zip(returned_signature.named.iter()) + .for_each(|(lhs, rhs)| assert_eq!(lhs, rhs)); + + assert_eq!( + signature.rest_positional, + returned_signature.rest_positional, + ); + } } diff --git a/crates/nu-plugin/src/serializers/value.rs b/crates/nu-plugin/src/serializers/value.rs index f13a5417a1..7ddb19bc98 100644 --- a/crates/nu-plugin/src/serializers/value.rs +++ b/crates/nu-plugin/src/serializers/value.rs @@ -1,6 +1,5 @@ -use crate::plugin::PluginError; use crate::plugin_capnp::value; -use nu_protocol::{Span, Value}; +use nu_protocol::{ShellError, Span, Value}; pub(crate) fn serialize_value(value: &Value, mut builder: value::Builder) { let value_span = match value { @@ -24,6 +23,22 @@ pub(crate) fn serialize_value(value: &Value, mut builder: value::Builder) { builder.set_string(val); *span } + Value::Record { cols, vals, span } => { + let mut record_builder = builder.reborrow().init_record(); + + let mut cols_builder = record_builder.reborrow().init_cols(cols.len() as u32); + cols.iter() + .enumerate() + .for_each(|(index, col)| cols_builder.set(index as u32, col.as_str())); + + let mut values_builder = record_builder.reborrow().init_vals(vals.len() as u32); + vals.iter().enumerate().for_each(|(index, value)| { + let inner_builder = values_builder.reborrow().get(index as u32); + serialize_value(value, inner_builder); + }); + + *span + } Value::List { vals, span } => { let mut list_builder = builder.reborrow().init_list(vals.len() as u32); for (index, value) in vals.iter().enumerate() { @@ -45,10 +60,10 @@ pub(crate) fn serialize_value(value: &Value, mut builder: value::Builder) { span.set_end(value_span.end as u64); } -pub(crate) fn deserialize_value(reader: value::Reader) -> Result { +pub(crate) fn deserialize_value(reader: value::Reader) -> Result { let span_reader = reader .get_span() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; let span = Span { start: span_reader.get_start() as usize, @@ -62,17 +77,39 @@ pub(crate) fn deserialize_value(reader: value::Reader) -> Result Ok(Value::Float { val, span }), Ok(value::String(val)) => { let string = val - .map_err(|e| PluginError::DecodingError(e.to_string()))? + .map_err(|e| ShellError::InternalError(e.to_string()))? .to_string(); Ok(Value::String { val: string, span }) } + Ok(value::Record(record)) => { + let record = record.map_err(|e| ShellError::InternalError(e.to_string()))?; + + let cols = record + .get_cols() + .map_err(|e| ShellError::InternalError(e.to_string()))? + .iter() + .map(|col| { + col.map_err(|e| ShellError::InternalError(e.to_string())) + .map(|col| col.to_string()) + }) + .collect::, ShellError>>()?; + + let vals = record + .get_vals() + .map_err(|e| ShellError::InternalError(e.to_string()))? + .iter() + .map(deserialize_value) + .collect::, ShellError>>()?; + + Ok(Value::Record { cols, vals, span }) + } Ok(value::List(vals)) => { - let values = vals.map_err(|e| PluginError::DecodingError(e.to_string()))?; + let values = vals.map_err(|e| ShellError::InternalError(e.to_string()))?; let values_list = values .iter() .map(deserialize_value) - .collect::, PluginError>>()?; + .collect::, ShellError>>()?; Ok(Value::List { vals: values_list, @@ -91,10 +128,7 @@ mod tests { use capnp::serialize; use nu_protocol::{Span, Value}; - pub fn write_buffer( - value: &Value, - writer: &mut impl std::io::Write, - ) -> Result<(), PluginError> { + pub fn write_buffer(value: &Value, writer: &mut impl std::io::Write) -> Result<(), ShellError> { let mut message = ::capnp::message::Builder::new_default(); let mut builder = message.init_root::(); @@ -102,16 +136,16 @@ mod tests { serialize_value(value, builder.reborrow()); serialize::write_message(writer, &message) - .map_err(|e| PluginError::EncodingError(e.to_string())) + .map_err(|e| ShellError::InternalError(e.to_string())) } - pub fn read_buffer(reader: &mut impl std::io::BufRead) -> Result { + pub fn read_buffer(reader: &mut impl std::io::BufRead) -> Result { let message_reader = serialize::read_message(reader, ::capnp::message::ReaderOptions::new()).unwrap(); let reader = message_reader .get_root::() - .map_err(|e| PluginError::DecodingError(e.to_string()))?; + .map_err(|e| ShellError::InternalError(e.to_string()))?; deserialize_value(reader.reborrow()) } @@ -262,4 +296,70 @@ mod tests { returned_value.span().expect("span") ) } + + #[test] + fn record_round_trip() { + let inner_values = vec![ + Value::Bool { + val: false, + span: Span { start: 1, end: 20 }, + }, + Value::Int { + val: 10, + span: Span { start: 2, end: 30 }, + }, + Value::Float { + val: 10.0, + span: Span { start: 3, end: 40 }, + }, + Value::String { + val: "inner string".into(), + span: Span { start: 4, end: 50 }, + }, + ]; + + let vals = vec![ + Value::Bool { + val: true, + span: Span { start: 1, end: 20 }, + }, + Value::Int { + val: 66, + span: Span { start: 2, end: 30 }, + }, + Value::Float { + val: 66.6, + span: Span { start: 3, end: 40 }, + }, + Value::String { + val: "a string".into(), + span: Span { start: 4, end: 50 }, + }, + Value::List { + vals: inner_values, + span: Span { start: 5, end: 60 }, + }, + ]; + + let cols = vec![ + "bool".to_string(), + "int".to_string(), + "float".to_string(), + "string".to_string(), + "list".to_string(), + ]; + + let record = Value::Record { + cols, + vals, + span: Span { start: 1, end: 20 }, + }; + + let mut buffer: Vec = Vec::new(); + write_buffer(&record, &mut buffer).expect("unable to serialize message"); + let returned_record = + read_buffer(&mut buffer.as_slice()).expect("unable to deserialize message"); + + assert_eq!(record, returned_record) + } } diff --git a/crates/nu-protocol/src/engine/command.rs b/crates/nu-protocol/src/engine/command.rs index 7f7bad6109..7b379db77c 100644 --- a/crates/nu-protocol/src/engine/command.rs +++ b/crates/nu-protocol/src/engine/command.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use crate::{ast::Call, BlockId, Example, PipelineData, ShellError, Signature}; use super::{EngineState, Stack}; @@ -47,7 +49,7 @@ pub trait Command: Send + Sync + CommandClone { } // Is a plugin command - fn is_plugin(&self) -> Option<&str> { + fn is_plugin(&self) -> Option<&PathBuf> { None } diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index 277a736cb0..d710bdc96a 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -222,26 +222,27 @@ impl EngineState { if let Some(plugin_path) = &self.plugin_signatures { // Always creating the file which will erase previous signatures let mut plugin_file = std::fs::File::create(plugin_path.as_path()) - .map_err(|err| ShellError::PluginError(err.to_string()))?; + .map_err(|err| ShellError::InternalError(err.to_string()))?; // Plugin definitions with parsed signature for decl in self.plugin_decls() { // A successful plugin registration already includes the plugin filename // No need to check the None option - let file_name = decl.is_plugin().expect("plugin should have file name"); + let path = decl.is_plugin().expect("plugin should have file name"); + let file_name = path.to_str().expect("path should be a str"); let line = serde_json::to_string_pretty(&decl.signature()) .map(|signature| format!("register {} {}\n", file_name, signature)) - .map_err(|err| ShellError::PluginError(err.to_string()))?; + .map_err(|err| ShellError::InternalError(err.to_string()))?; plugin_file .write_all(line.as_bytes()) - .map_err(|err| ShellError::PluginError(err.to_string()))?; + .map_err(|err| ShellError::InternalError(err.to_string()))?; } Ok(()) } else { - Err(ShellError::PluginError("Plugin file not found".into())) + Err(ShellError::InternalError("Plugin file not found".into())) } } @@ -510,11 +511,13 @@ pub struct StateDelta { pub(crate) file_contents: Vec<(Vec, usize, usize)>, vars: Vec, // indexed by VarId decls: Vec>, // indexed by DeclId - #[cfg(feature = "plugin")] - plugin_decls: Vec>, // indexed by DeclId blocks: Vec, // indexed by BlockId overlays: Vec, // indexed by OverlayId pub scope: Vec, + #[cfg(feature = "plugin")] + pub plugin_signatures: Vec<(PathBuf, Option)>, + #[cfg(feature = "plugin")] + plugin_decls: Vec>, } impl StateDelta { @@ -551,11 +554,13 @@ impl<'a> StateWorkingSet<'a> { file_contents: vec![], vars: vec![], decls: vec![], - #[cfg(feature = "plugin")] - plugin_decls: vec![], blocks: vec![], overlays: vec![], scope: vec![ScopeFrame::new()], + #[cfg(feature = "plugin")] + plugin_signatures: vec![], + #[cfg(feature = "plugin")] + plugin_decls: vec![], }, permanent_state, } @@ -624,8 +629,20 @@ impl<'a> StateWorkingSet<'a> { } #[cfg(feature = "plugin")] - pub fn add_plugin_decl(&mut self, decl: Box) { - self.delta.plugin_decls.push(decl); + pub fn add_plugin_decls(&mut self, decls: Vec>) { + for decl in decls { + self.delta.plugin_decls.push(decl); + } + } + + #[cfg(feature = "plugin")] + pub fn add_plugin_signature(&mut self, path: PathBuf, signature: Option) { + self.delta.plugin_signatures.push((path, signature)); + } + + #[cfg(feature = "plugin")] + pub fn get_signatures(&self) -> impl Iterator)> { + self.delta.plugin_signatures.iter() } pub fn merge_predecl(&mut self, name: &[u8]) -> Option { diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index 56def77c4a..16159b06da 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -200,9 +200,6 @@ pub enum ShellError { #[error("No file to be copied")] NoFileToBeCopied(), - #[error("Plugin error")] - PluginError(String), - #[error("Name not found")] #[diagnostic(code(nu::shell::name_not_found), url(docsrs))] DidYouMean(String, #[label("did you mean '{0}'?")] Span), diff --git a/crates/nu-engine/src/from_value.rs b/crates/nu-protocol/src/value/from_value.rs similarity index 84% rename from crates/nu-engine/src/from_value.rs rename to crates/nu-protocol/src/value/from_value.rs index 54dfc100bc..2d07fc6270 100644 --- a/crates/nu-engine/src/from_value.rs +++ b/crates/nu-protocol/src/value/from_value.rs @@ -5,9 +5,9 @@ use std::str::FromStr; use chrono::{DateTime, FixedOffset}; // use nu_path::expand_path; -use nu_protocol::ast::{CellPath, PathMember}; -use nu_protocol::ShellError; -use nu_protocol::{Range, Spanned, Value}; +use crate::ast::{CellPath, PathMember}; +use crate::ShellError; +use crate::{Range, Spanned, Value}; pub trait FromValue: Sized { fn from_value(v: &Value) -> Result; @@ -337,53 +337,3 @@ impl FromValue for Spanned { } } } - -// impl FromValue for Dictionary { -// fn from_value(v: &Value) -> Result { -// match v { -// Value { -// value: UntaggedValue::Row(r), -// .. -// } => Ok(r.clone()), -// v => Err(ShellError::type_error("row", v.spanned_type_name())), -// } -// } -// } - -// impl FromValue for CapturedBlock { -// fn from_value(v: &Value) -> Result { -// match v { -// Value { -// value: UntaggedValue::Block(b), -// .. -// } => Ok((**b).clone()), -// Value { -// value: UntaggedValue::Row(_), -// .. -// } => { -// let mut shell_error = ShellError::type_error("block", v.spanned_type_name()); -// shell_error.notes.push( -// "Note: you can access columns using dot. eg) $it.column or (ls).column".into(), -// ); -// Err(shell_error) -// } -// v => Err(ShellError::type_error("block", v.spanned_type_name())), -// } -// } -// } - -// impl FromValue for Vec { -// fn from_value(v: &Value) -> Result { -// match v { -// Value { -// value: UntaggedValue::Table(t), -// .. -// } => Ok(t.clone()), -// Value { -// value: UntaggedValue::Row(_), -// .. -// } => Ok(vec![v.clone()]), -// v => Err(ShellError::type_error("table", v.spanned_type_name())), -// } -// } -// } diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index 8e041bcaea..93fdf99f98 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -1,11 +1,13 @@ mod custom_value; mod from; +mod from_value; mod range; mod stream; mod unit; use chrono::{DateTime, FixedOffset}; use chrono_humanize::HumanTime; +pub use from_value::FromValue; use indexmap::map::IndexMap; pub use range::*; use serde::{Deserialize, Serialize}; diff --git a/crates/nu_plugin_example/Cargo.toml b/crates/nu_plugin_example/Cargo.toml new file mode 100644 index 0000000000..250b432208 --- /dev/null +++ b/crates/nu_plugin_example/Cargo.toml @@ -0,0 +1,11 @@ +[package] +authors = ["The Nu Project Contributors"] +description = "A version incrementer plugin for Nushell" +edition = "2018" +license = "MIT" +name = "nu_plugin_example" +version = "0.1.0" + +[dependencies] +nu-plugin = { path="../nu-plugin", version = "0.1.0" } +nu-protocol = { path="../nu-protocol", version = "0.1.0", features = ["plugin"]} diff --git a/crates/nu_plugin_example/README.md b/crates/nu_plugin_example/README.md new file mode 100644 index 0000000000..54df1602b8 --- /dev/null +++ b/crates/nu_plugin_example/README.md @@ -0,0 +1,4 @@ +# Plugin Example + +Crate with a simple example of the Plugin trait that needs to be implemented +in order to create a binary that can be registered into nushell declaration list diff --git a/crates/nu_plugin_example/src/main.rs b/crates/nu_plugin_example/src/main.rs new file mode 100644 index 0000000000..f43c5046fb --- /dev/null +++ b/crates/nu_plugin_example/src/main.rs @@ -0,0 +1,145 @@ +use nu_plugin::{serve_plugin, EvaluatedCall, Plugin}; +use nu_protocol::{Category, ShellError, Signature, SyntaxShape, Value}; + +fn main() { + serve_plugin(&mut Example {}) +} + +struct Example {} + +impl Plugin for Example { + fn signature(&self) -> Vec { + // It is possible to declare multiple signature in a plugin + // Each signature will be converted to a command declaration once the + // plugin is registered to nushell + vec![ + Signature::build("test-1") + .desc("Signature test 1 for plugin. Returns Value::Nothing") + .required("a", SyntaxShape::Int, "required integer value") + .required("b", SyntaxShape::String, "required string value") + .switch("flag", "a flag for the signature", Some('f')) + .optional("opt", SyntaxShape::Int, "Optional number") + .named("named", SyntaxShape::String, "named string", Some('n')) + .rest("rest", SyntaxShape::String, "rest value string") + .category(Category::Experimental), + Signature::build("test-2") + .desc("Signature test 2 for plugin. Returns list of records") + .required("a", SyntaxShape::Int, "required integer value") + .required("b", SyntaxShape::String, "required string value") + .switch("flag", "a flag for the signature", Some('f')) + .optional("opt", SyntaxShape::Int, "Optional number") + .named("named", SyntaxShape::String, "named string", Some('n')) + .rest("rest", SyntaxShape::Int, "rest value int") + .category(Category::Experimental), + ] + } + + fn run( + &mut self, + name: &str, + call: &EvaluatedCall, + input: &Value, + ) -> Result { + // You can use the name to identify what plugin signature was called + match name { + "test-1" => test1(call, input), + "test-2" => test2(call, input), + _ => Err(ShellError::InternalError( + "Plugin call with wrong name signature".into(), + )), + } + } +} + +fn test1(call: &EvaluatedCall, input: &Value) -> Result { + // Note. When debugging your plugin, you may want to print something to the console + // Use the eprintln macro to print your messages. Trying to print to stdout will + // cause a decoding error for your message + eprintln!("Calling test1 signature"); + eprintln!("value received {:?}", input); + + // To extract the arguments from the Call object you can use the functions req, has_flag, + // opt, rest, and get_flag + // + // Note that plugin calls only accept simple arguments, this means that you can + // pass to the plug in Int and String. This should be improved when the plugin has + // the ability to call back to NuShell to extract more information + // Keep this in mind when designing your plugin signatures + let a: i64 = call.req(0)?; + let b: String = call.req(1)?; + let flag = call.has_flag("flag"); + let opt: Option = call.opt(2)?; + let named: Option = call.get_flag("named")?; + let rest: Vec = call.rest(3)?; + + eprintln!("Required values"); + eprintln!("a: {:}", a); + eprintln!("b: {:}", b); + eprintln!("flag: {:}", flag); + eprintln!("rest: {:?}", rest); + + match opt { + Some(v) => eprintln!("Found optional value opt: {:}", v), + None => eprintln!("No optional value found"), + } + + match named { + Some(v) => eprintln!("Named value: {:?}", v), + None => eprintln!("No named value found"), + } + + Ok(Value::Nothing { span: call.head }) +} + +fn test2(call: &EvaluatedCall, input: &Value) -> Result { + eprintln!("Calling test1 signature"); + eprintln!("value received {:?}", input); + + eprintln!("Arguments received"); + let a: i64 = call.req(0)?; + let b: String = call.req(1)?; + let flag = call.has_flag("flag"); + let opt: Option = call.opt(2)?; + let named: Option = call.get_flag("named")?; + let rest: Vec = call.rest(3)?; + + eprintln!("Required values"); + eprintln!("a: {:}", a); + eprintln!("b: {:}", b); + eprintln!("flag: {:}", flag); + eprintln!("rest: {:?}", rest); + + match opt { + Some(v) => eprintln!("Found optional value opt: {:}", v), + None => eprintln!("No optional value found"), + } + + match named { + Some(v) => eprintln!("Named value: {:?}", v), + None => eprintln!("No named value found"), + } + + let cols = vec!["one".to_string(), "two".to_string(), "three".to_string()]; + + let vals = (0..10i64) + .map(|i| { + let vals = (0..3) + .map(|v| Value::Int { + val: v * i, + span: call.head.clone(), + }) + .collect::>(); + + Value::Record { + cols: cols.clone(), + vals, + span: call.head.clone(), + } + }) + .collect::>(); + + Ok(Value::List { + vals, + span: call.head, + }) +} diff --git a/crates/nu_plugin_inc/Cargo.toml b/crates/nu_plugin_inc/Cargo.toml index b3f4702268..fe734ae498 100644 --- a/crates/nu_plugin_inc/Cargo.toml +++ b/crates/nu_plugin_inc/Cargo.toml @@ -11,7 +11,6 @@ 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" } +nu-protocol = { path="../nu-protocol", version = "0.1.0", features = ["plugin"]} semver = "0.11.0" diff --git a/crates/nu_plugin_inc/src/inc.rs b/crates/nu_plugin_inc/src/inc.rs index 958c1e68c6..5d491d6b02 100644 --- a/crates/nu_plugin_inc/src/inc.rs +++ b/crates/nu_plugin_inc/src/inc.rs @@ -1,5 +1,4 @@ -use nu_plugin::plugin::PluginError; -use nu_protocol::{Span, Value}; +use nu_protocol::{ShellError, Span, Value}; #[derive(Debug, Eq, PartialEq)] pub enum Action { @@ -82,14 +81,14 @@ impl Inc { "Usage: inc field [--major|--minor|--patch]" } - pub fn inc(&self, value: &Value) -> Result { + pub fn inc(&self, value: &Value) -> Result { match value { Value::Int { val, span } => Ok(Value::Int { val: val + 1, span: *span, }), Value::String { val, .. } => Ok(self.apply(val)), - _ => Err(PluginError::RunTimeError("incrementable value".to_string())), + _ => Err(ShellError::InternalError("incrementable value".to_string())), } } } diff --git a/crates/nu_plugin_inc/src/nu/mod.rs b/crates/nu_plugin_inc/src/nu/mod.rs index 8eaf2fc9f0..d8b87ca6c1 100644 --- a/crates/nu_plugin_inc/src/nu/mod.rs +++ b/crates/nu_plugin_inc/src/nu/mod.rs @@ -1,8 +1,7 @@ use crate::inc::SemVerAction; use crate::Inc; -use nu_plugin::{plugin::PluginError, Plugin}; -use nu_protocol::ast::Call; -use nu_protocol::{Signature, Span, Value}; +use nu_plugin::{EvaluatedCall, Plugin}; +use nu_protocol::{ShellError, Signature, Span, Value}; impl Plugin for Inc { fn signature(&self) -> Vec { @@ -25,7 +24,12 @@ impl Plugin for Inc { )] } - fn run(&mut self, name: &str, call: &Call, input: &Value) -> Result { + fn run( + &mut self, + name: &str, + call: &EvaluatedCall, + input: &Value, + ) -> Result { if name != "inc" { return Ok(Value::Nothing { span: Span::unknown(), diff --git a/src/main.rs b/src/main.rs index 30e51453df..974fb4be86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,9 @@ use nu_protocol::{ }; use reedline::{Completer, CompletionActionHandler, DefaultPrompt, LineBuffer, Prompt}; +#[cfg(feature = "plugin")] +use nu_plugin::plugin::eval_plugin_signatures; + #[cfg(test)] mod tests; @@ -412,6 +415,12 @@ fn eval_source( report_error(&working_set, &err); return false; } + + #[cfg(feature = "plugin")] + if let Err(err) = eval_plugin_signatures(&mut working_set) { + report_error(&working_set, &err); + } + (output, working_set.render()) };