diff --git a/Cargo.lock b/Cargo.lock index 03e499e99..bcec8fb5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,6 +271,15 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.59.2" @@ -2720,6 +2729,7 @@ dependencies = [ name = "nu-plugin" version = "0.65.1" dependencies = [ + "bincode", "capnp", "nu-engine", "nu-protocol", @@ -2809,6 +2819,16 @@ dependencies = [ "lscolors", ] +[[package]] +name = "nu_plugin_custom_values" +version = "0.1.0" +dependencies = [ + "nu-plugin", + "nu-protocol", + "serde", + "typetag", +] + [[package]] name = "nu_plugin_example" version = "0.65.1" diff --git a/Cargo.toml b/Cargo.toml index a1f3f9cb4..0ffa80973 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ members = [ "crates/nu_plugin_gstat", "crates/nu_plugin_example", "crates/nu_plugin_query", + "crates/nu_plugin_custom_values", "crates/nu-utils", ] diff --git a/crates/nu-plugin/Cargo.toml b/crates/nu-plugin/Cargo.toml index 32c3baa51..71b137065 100644 --- a/crates/nu-plugin/Cargo.toml +++ b/crates/nu-plugin/Cargo.toml @@ -7,6 +7,7 @@ name = "nu-plugin" version = "0.65.1" [dependencies] +bincode = "1.3.3" capnp = "0.14.3" nu-protocol = { path = "../nu-protocol", version = "0.65.1" } nu-engine = { path = "../nu-engine", version = "0.65.1" } diff --git a/crates/nu-plugin/src/lib.rs b/crates/nu-plugin/src/lib.rs index de50aa1ff..e0a58ee56 100644 --- a/crates/nu-plugin/src/lib.rs +++ b/crates/nu-plugin/src/lib.rs @@ -6,5 +6,5 @@ mod serializers; mod plugin_capnp; pub use plugin::{get_signature, serve_plugin, Plugin, PluginDeclaration}; -pub use protocol::{EvaluatedCall, LabeledError}; +pub use protocol::{EvaluatedCall, LabeledError, PluginData}; pub use serializers::{capnp::CapnpSerializer, json::JsonSerializer, EncodingType}; diff --git a/crates/nu-plugin/src/plugin/declaration.rs b/crates/nu-plugin/src/plugin/declaration.rs index 6221225ab..31c52d696 100644 --- a/crates/nu-plugin/src/plugin/declaration.rs +++ b/crates/nu-plugin/src/plugin/declaration.rs @@ -1,13 +1,14 @@ use crate::{EncodingType, EvaluatedCall}; -use super::{create_command, OUTPUT_BUFFER_SIZE}; -use crate::protocol::{CallInfo, PluginCall, PluginResponse}; -use std::io::BufReader; +use super::{call_plugin, create_command}; +use crate::protocol::{ + CallInfo, CallInput, PluginCall, PluginCustomValue, PluginData, PluginResponse, +}; use std::path::{Path, PathBuf}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ast::Call, Signature}; -use nu_protocol::{PipelineData, ShellError}; +use nu_protocol::{PipelineData, ShellError, Value}; #[derive(Clone)] pub struct PluginDeclaration { @@ -73,28 +74,41 @@ impl Command for PluginDeclaration { })?; let input = input.into_value(call.head); + let input = match input { + Value::CustomValue { val, span } => { + match val.as_any().downcast_ref::() { + Some(plugin_data) if plugin_data.filename == self.filename => { + CallInput::Data(PluginData { + data: plugin_data.data.clone(), + span, + }) + } + _ => { + let custom_value_name = val.value_string(); + return Err(ShellError::GenericError( + format!( + "Plugin {} can not handle the custom value {}", + self.name, custom_value_name + ), + format!("custom value {}", custom_value_name), + Some(span), + None, + Vec::new(), + )); + } + } + } + value => CallInput::Value(value), + }; - // Create message to plugin to indicate that signature is required and - // send call to plugin asking for signature - if let Some(mut stdin_writer) = child.stdin.take() { - let encoding_clone = self.encoding.clone(); - let plugin_call = PluginCall::CallInfo(Box::new(CallInfo { - name: self.name.clone(), - call: EvaluatedCall::try_from_call(call, engine_state, stack)?, - input, - })); - std::thread::spawn(move || { - // PluginCall information - encoding_clone.encode_call(&plugin_call, &mut stdin_writer) - }); - } + let plugin_call = PluginCall::CallInfo(CallInfo { + name: self.name.clone(), + call: EvaluatedCall::try_from_call(call, engine_state, stack)?, + input, + }); - // 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 = self.encoding.decode_response(&mut buf_read).map_err(|err| { + let response = + call_plugin(&mut child, plugin_call, &self.encoding, call.head).map_err(|err| { let decl = engine_state.get_decl(call.decl_id); ShellError::GenericError( format!("Unable to decode call for {}", decl.name()), @@ -105,28 +119,33 @@ impl Command for PluginDeclaration { ) }); - match response { - Ok(PluginResponse::Value(value)) => { - Ok(PipelineData::Value(value.as_ref().clone(), None)) - } - Ok(PluginResponse::Error(err)) => Err(err.into()), - Ok(PluginResponse::Signature(..)) => Err(ShellError::GenericError( - "Plugin missing value".into(), - "Received a signature from plugin instead of value".into(), - Some(call.head), - None, - Vec::new(), - )), - Err(err) => Err(err), + let pipeline_data = match response { + Ok(PluginResponse::Value(value)) => { + Ok(PipelineData::Value(value.as_ref().clone(), None)) } - } else { - Err(ShellError::GenericError( - "Error with stdout reader".into(), - "no stdout reader".into(), + Ok(PluginResponse::PluginData(name, plugin_data)) => Ok(PipelineData::Value( + Value::CustomValue { + val: Box::new(PluginCustomValue { + name, + data: plugin_data.data, + filename: self.filename.clone(), + shell: self.shell.clone(), + encoding: self.encoding.clone(), + source: engine_state.get_decl(call.decl_id).name().to_owned(), + }), + span: plugin_data.span, + }, + None, + )), + Ok(PluginResponse::Error(err)) => Err(err.into()), + Ok(PluginResponse::Signature(..)) => Err(ShellError::GenericError( + "Plugin missing value".into(), + "Received a signature from plugin instead of value".into(), Some(call.head), None, Vec::new(), - )) + )), + Err(err) => Err(err), }; // We need to call .wait() on the child, or we'll risk summoning the zombie horde diff --git a/crates/nu-plugin/src/plugin/mod.rs b/crates/nu-plugin/src/plugin/mod.rs index 91e1e1657..9c43d98a0 100644 --- a/crates/nu-plugin/src/plugin/mod.rs +++ b/crates/nu-plugin/src/plugin/mod.rs @@ -1,18 +1,18 @@ mod declaration; pub use declaration::PluginDeclaration; -use crate::protocol::{LabeledError, PluginCall, PluginResponse}; +use crate::protocol::{CallInput, LabeledError, PluginCall, PluginData, PluginResponse}; use crate::EncodingType; use std::io::BufReader; use std::path::{Path, PathBuf}; -use std::process::{Command as CommandSys, Stdio}; +use std::process::{Child, Command as CommandSys, Stdio}; -use nu_protocol::ShellError; +use nu_protocol::{CustomValue, ShellError, Span}; use nu_protocol::{Signature, Value}; use super::EvaluatedCall; -const OUTPUT_BUFFER_SIZE: usize = 8192; +pub(crate) const OUTPUT_BUFFER_SIZE: usize = 8192; pub trait PluginEncoder: Clone { fn encode_call( @@ -35,7 +35,7 @@ pub trait PluginEncoder: Clone { ) -> Result; } -fn create_command(path: &Path, shell: &Option) -> CommandSys { +pub(crate) fn create_command(path: &Path, shell: &Option) -> CommandSys { let mut process = match (path.extension(), shell) { (_, Some(shell)) => { let mut process = std::process::Command::new(shell); @@ -77,6 +77,37 @@ fn create_command(path: &Path, shell: &Option) -> CommandSys { process } +pub(crate) fn call_plugin( + child: &mut Child, + plugin_call: PluginCall, + encoding: &EncodingType, + span: Span, +) -> Result { + if let Some(mut stdin_writer) = child.stdin.take() { + let encoding_clone = encoding.clone(); + std::thread::spawn(move || { + // PluginCall information + encoding_clone.encode_call(&plugin_call, &mut stdin_writer) + }); + } + + // Deserialize response from plugin to extract the resulting value + if let Some(stdout_reader) = &mut child.stdout { + let reader = stdout_reader; + let mut buf_read = BufReader::with_capacity(OUTPUT_BUFFER_SIZE, reader); + + encoding.decode_response(&mut buf_read) + } else { + Err(ShellError::GenericError( + "Error with stdout reader".into(), + "no stdout reader".into(), + Some(span), + None, + Vec::new(), + )) + } +} + pub fn get_signature( path: &Path, encoding: &EncodingType, @@ -172,9 +203,33 @@ pub fn serve_plugin(plugin: &mut impl Plugin, encoder: impl PluginEncoder) { .expect("Error encoding response"); } PluginCall::CallInfo(call_info) => { - let value = plugin.run(&call_info.name, &call_info.call, &call_info.input); + let input = match call_info.input { + CallInput::Value(value) => Ok(value), + CallInput::Data(plugin_data) => { + bincode::deserialize::>(&plugin_data.data) + .map(|custom_value| Value::CustomValue { + val: custom_value, + span: plugin_data.span, + }) + .map_err(|err| ShellError::PluginFailedToDecode(err.to_string())) + } + }; + + let value = match input { + Ok(input) => plugin.run(&call_info.name, &call_info.call, &input), + Err(err) => Err(err.into()), + }; let response = match value { + Ok(Value::CustomValue { val, span }) => match bincode::serialize(&val) { + Ok(data) => { + let name = val.value_string(); + PluginResponse::PluginData(name, PluginData { data, span }) + } + Err(err) => PluginResponse::Error( + ShellError::PluginFailedToEncode(err.to_string()).into(), + ), + }, Ok(value) => PluginResponse::Value(Box::new(value)), Err(err) => PluginResponse::Error(err), }; @@ -182,6 +237,18 @@ pub fn serve_plugin(plugin: &mut impl Plugin, encoder: impl PluginEncoder) { .encode_response(&response, &mut std::io::stdout()) .expect("Error encoding response"); } + PluginCall::CollapseCustomValue(plugin_data) => { + let response = bincode::deserialize::>(&plugin_data.data) + .map_err(|err| ShellError::PluginFailedToDecode(err.to_string())) + .and_then(|val| val.to_base_value(plugin_data.span)) + .map(Box::new) + .map_err(LabeledError::from) + .map_or_else(PluginResponse::Error, PluginResponse::Value); + + encoder + .encode_response(&response, &mut std::io::stdout()) + .expect("Error encoding response"); + } } } } diff --git a/crates/nu-plugin/src/plugin_capnp.rs b/crates/nu-plugin/src/plugin_capnp.rs index 9e06c11ca..4598cef02 100644 --- a/crates/nu-plugin/src/plugin_capnp.rs +++ b/crates/nu-plugin/src/plugin_capnp.rs @@ -105,12 +105,14 @@ pub mod err { pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> { self.reader.total_size() } + #[inline] pub fn has_err(&self) -> bool { if self.reader.get_data_field::(0) != 0 { return false; } !self.reader.get_pointer_field(0).is_null() } + #[inline] pub fn has_ok(&self) -> bool { if self.reader.get_data_field::(0) != 1 { return false; @@ -244,6 +246,7 @@ pub mod err { self.builder.set_data_field::(0, 0); self.builder.get_pointer_field(0).init_text(size) } + #[inline] pub fn has_err(&self) -> bool { if self.builder.get_data_field::(0) != 0 { return false; @@ -272,6 +275,7 @@ pub mod err { self.builder.set_data_field::(0, 1); ::capnp::any_pointer::Builder::new(self.builder.get_pointer_field(0)).init_as() } + #[inline] pub fn has_ok(&self) -> bool { if self.builder.get_data_field::(0) != 1 { return false; @@ -460,6 +464,7 @@ pub mod map { ::core::option::Option::None, ) } + #[inline] pub fn has_entries(&self) -> bool { !self.reader.get_pointer_field(0).is_null() } @@ -606,6 +611,7 @@ pub mod map { size, ) } + #[inline] pub fn has_entries(&self) -> bool { !self.builder.get_pointer_field(0).is_null() } @@ -758,6 +764,7 @@ pub mod map { ::core::option::Option::None, ) } + #[inline] pub fn has_key(&self) -> bool { !self.reader.get_pointer_field(0).is_null() } @@ -770,6 +777,7 @@ pub mod map { ::core::option::Option::None, ) } + #[inline] pub fn has_value(&self) -> bool { !self.reader.get_pointer_field(1).is_null() } @@ -909,6 +917,7 @@ pub mod map { pub fn init_key(self) -> >::Builder { ::capnp::any_pointer::Builder::new(self.builder.get_pointer_field(0)).init_as() } + #[inline] pub fn has_key(&self) -> bool { !self.builder.get_pointer_field(0).is_null() } @@ -944,6 +953,7 @@ pub mod map { pub fn init_value(self) -> >::Builder { ::capnp::any_pointer::Builder::new(self.builder.get_pointer_field(1)).init_as() } + #[inline] pub fn has_value(&self) -> bool { !self.builder.get_pointer_field(1).is_null() } @@ -1243,21 +1253,25 @@ pub mod value { ::core::option::Option::None, ) } + #[inline] pub fn has_span(&self) -> bool { !self.reader.get_pointer_field(0).is_null() } + #[inline] pub fn has_string(&self) -> bool { if self.reader.get_data_field::(0) != 4 { return false; } !self.reader.get_pointer_field(1).is_null() } + #[inline] pub fn has_list(&self) -> bool { if self.reader.get_data_field::(0) != 5 { return false; } !self.reader.get_pointer_field(1).is_null() } + #[inline] pub fn has_record(&self) -> bool { if self.reader.get_data_field::(0) != 6 { return false; @@ -1385,6 +1399,7 @@ pub mod value { pub fn init_span(self) -> crate::plugin_capnp::span::Builder<'a> { ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0) } + #[inline] pub fn has_span(&self) -> bool { !self.builder.get_pointer_field(0).is_null() } @@ -1417,6 +1432,7 @@ pub mod value { self.builder.set_data_field::(0, 4); self.builder.get_pointer_field(1).init_text(size) } + #[inline] pub fn has_string(&self) -> bool { if self.builder.get_data_field::(0) != 4 { return false; @@ -1446,6 +1462,7 @@ pub mod value { size, ) } + #[inline] pub fn has_list(&self) -> bool { if self.builder.get_data_field::(0) != 5 { return false; @@ -1469,6 +1486,7 @@ pub mod value { self.builder.set_data_field::(0, 6); ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(1), 0) } + #[inline] pub fn has_record(&self) -> bool { if self.builder.get_data_field::(0) != 6 { return false; @@ -1620,6 +1638,7 @@ pub mod record { ::core::option::Option::None, ) } + #[inline] pub fn has_cols(&self) -> bool { !self.reader.get_pointer_field(0).is_null() } @@ -1633,6 +1652,7 @@ pub mod record { ::core::option::Option::None, ) } + #[inline] pub fn has_vals(&self) -> bool { !self.reader.get_pointer_field(1).is_null() } @@ -1729,6 +1749,7 @@ pub mod record { size, ) } + #[inline] pub fn has_cols(&self) -> bool { !self.builder.get_pointer_field(0).is_null() } @@ -1763,6 +1784,7 @@ pub mod record { size, ) } + #[inline] pub fn has_vals(&self) -> bool { !self.builder.get_pointer_field(1).is_null() } @@ -1860,6 +1882,7 @@ pub mod signature { ::core::option::Option::None, ) } + #[inline] pub fn has_name(&self) -> bool { !self.reader.get_pointer_field(0).is_null() } @@ -1870,6 +1893,7 @@ pub mod signature { ::core::option::Option::None, ) } + #[inline] pub fn has_usage(&self) -> bool { !self.reader.get_pointer_field(1).is_null() } @@ -1880,6 +1904,7 @@ pub mod signature { ::core::option::Option::None, ) } + #[inline] pub fn has_extra_usage(&self) -> bool { !self.reader.get_pointer_field(2).is_null() } @@ -1890,6 +1915,7 @@ pub mod signature { ::core::option::Option::None, ) } + #[inline] pub fn has_search_terms(&self) -> bool { !self.reader.get_pointer_field(3).is_null() } @@ -1903,6 +1929,7 @@ pub mod signature { ::core::option::Option::None, ) } + #[inline] pub fn has_required_positional(&self) -> bool { !self.reader.get_pointer_field(4).is_null() } @@ -1916,6 +1943,7 @@ pub mod signature { ::core::option::Option::None, ) } + #[inline] pub fn has_optional_positional(&self) -> bool { !self.reader.get_pointer_field(5).is_null() } @@ -1926,6 +1954,7 @@ pub mod signature { ::core::option::Option::None, ) } + #[inline] pub fn has_rest(&self) -> bool { !self.reader.get_pointer_field(6).is_null() } @@ -1939,6 +1968,7 @@ pub mod signature { ::core::option::Option::None, ) } + #[inline] pub fn has_named(&self) -> bool { !self.reader.get_pointer_field(7).is_null() } @@ -2038,6 +2068,7 @@ pub mod signature { pub fn init_name(self, size: u32) -> ::capnp::text::Builder<'a> { self.builder.get_pointer_field(0).init_text(size) } + #[inline] pub fn has_name(&self) -> bool { !self.builder.get_pointer_field(0).is_null() } @@ -2056,6 +2087,7 @@ pub mod signature { pub fn init_usage(self, size: u32) -> ::capnp::text::Builder<'a> { self.builder.get_pointer_field(1).init_text(size) } + #[inline] pub fn has_usage(&self) -> bool { !self.builder.get_pointer_field(1).is_null() } @@ -2074,6 +2106,7 @@ pub mod signature { pub fn init_extra_usage(self, size: u32) -> ::capnp::text::Builder<'a> { self.builder.get_pointer_field(2).init_text(size) } + #[inline] pub fn has_extra_usage(&self) -> bool { !self.builder.get_pointer_field(2).is_null() } @@ -2102,6 +2135,7 @@ pub mod signature { size, ) } + #[inline] pub fn has_search_terms(&self) -> bool { !self.builder.get_pointer_field(3).is_null() } @@ -2136,6 +2170,7 @@ pub mod signature { size, ) } + #[inline] pub fn has_required_positional(&self) -> bool { !self.builder.get_pointer_field(4).is_null() } @@ -2170,6 +2205,7 @@ pub mod signature { size, ) } + #[inline] pub fn has_optional_positional(&self) -> bool { !self.builder.get_pointer_field(5).is_null() } @@ -2195,6 +2231,7 @@ pub mod signature { pub fn init_rest(self) -> crate::plugin_capnp::argument::Builder<'a> { ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(6), 0) } + #[inline] pub fn has_rest(&self) -> bool { !self.builder.get_pointer_field(6).is_null() } @@ -2229,6 +2266,7 @@ pub mod signature { size, ) } + #[inline] pub fn has_named(&self) -> bool { !self.builder.get_pointer_field(7).is_null() } @@ -2411,6 +2449,7 @@ pub mod flag { ::core::option::Option::None, ) } + #[inline] pub fn has_long(&self) -> bool { !self.reader.get_pointer_field(0).is_null() } @@ -2421,6 +2460,7 @@ pub mod flag { ::core::option::Option::None, ) } + #[inline] pub fn has_short(&self) -> bool { !self.reader.get_pointer_field(1).is_null() } @@ -2441,6 +2481,7 @@ pub mod flag { ::core::option::Option::None, ) } + #[inline] pub fn has_desc(&self) -> bool { !self.reader.get_pointer_field(2).is_null() } @@ -2530,6 +2571,7 @@ pub mod flag { pub fn init_long(self, size: u32) -> ::capnp::text::Builder<'a> { self.builder.get_pointer_field(0).init_text(size) } + #[inline] pub fn has_long(&self) -> bool { !self.builder.get_pointer_field(0).is_null() } @@ -2548,6 +2590,7 @@ pub mod flag { pub fn init_short(self, size: u32) -> ::capnp::text::Builder<'a> { self.builder.get_pointer_field(1).init_text(size) } + #[inline] pub fn has_short(&self) -> bool { !self.builder.get_pointer_field(1).is_null() } @@ -2584,6 +2627,7 @@ pub mod flag { pub fn init_desc(self, size: u32) -> ::capnp::text::Builder<'a> { self.builder.get_pointer_field(2).init_text(size) } + #[inline] pub fn has_desc(&self) -> bool { !self.builder.get_pointer_field(2).is_null() } @@ -2681,6 +2725,7 @@ pub mod argument { ::core::option::Option::None, ) } + #[inline] pub fn has_name(&self) -> bool { !self.reader.get_pointer_field(0).is_null() } @@ -2691,6 +2736,7 @@ pub mod argument { ::core::option::Option::None, ) } + #[inline] pub fn has_desc(&self) -> bool { !self.reader.get_pointer_field(1).is_null() } @@ -2786,6 +2832,7 @@ pub mod argument { pub fn init_name(self, size: u32) -> ::capnp::text::Builder<'a> { self.builder.get_pointer_field(0).init_text(size) } + #[inline] pub fn has_name(&self) -> bool { !self.builder.get_pointer_field(0).is_null() } @@ -2804,6 +2851,7 @@ pub mod argument { pub fn init_desc(self, size: u32) -> ::capnp::text::Builder<'a> { self.builder.get_pointer_field(1).init_text(size) } + #[inline] pub fn has_desc(&self) -> bool { !self.builder.get_pointer_field(1).is_null() } @@ -2948,6 +2996,7 @@ pub mod evaluated_call { ::core::option::Option::None, ) } + #[inline] pub fn has_head(&self) -> bool { !self.reader.get_pointer_field(0).is_null() } @@ -2961,6 +3010,7 @@ pub mod evaluated_call { ::core::option::Option::None, ) } + #[inline] pub fn has_positional(&self) -> bool { !self.reader.get_pointer_field(1).is_null() } @@ -2979,6 +3029,7 @@ pub mod evaluated_call { ::core::option::Option::None, ) } + #[inline] pub fn has_named(&self) -> bool { !self.reader.get_pointer_field(2).is_null() } @@ -3075,6 +3126,7 @@ pub mod evaluated_call { pub fn init_head(self) -> crate::plugin_capnp::span::Builder<'a> { ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0) } + #[inline] pub fn has_head(&self) -> bool { !self.builder.get_pointer_field(0).is_null() } @@ -3109,6 +3161,7 @@ pub mod evaluated_call { size, ) } + #[inline] pub fn has_positional(&self) -> bool { !self.builder.get_pointer_field(1).is_null() } @@ -3156,6 +3209,7 @@ pub mod evaluated_call { > { ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(2), 0) } + #[inline] pub fn has_named(&self) -> bool { !self.builder.get_pointer_field(2).is_null() } @@ -3194,6 +3248,505 @@ pub mod evaluated_call { } } +pub mod plugin_data { + #[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_data(self) -> ::capnp::Result<::capnp::data::Reader<'a>> { + ::capnp::traits::FromPointerReader::get_from_pointer( + &self.reader.get_pointer_field(0), + ::core::option::Option::None, + ) + } + #[inline] + pub fn has_data(&self) -> bool { + !self.reader.get_pointer_field(0).is_null() + } + #[inline] + pub fn get_span(self) -> ::capnp::Result> { + ::capnp::traits::FromPointerReader::get_from_pointer( + &self.reader.get_pointer_field(1), + ::core::option::Option::None, + ) + } + #[inline] + pub fn has_span(&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_data(self) -> ::capnp::Result<::capnp::data::Builder<'a>> { + ::capnp::traits::FromPointerBuilder::get_from_pointer( + self.builder.get_pointer_field(0), + ::core::option::Option::None, + ) + } + #[inline] + pub fn set_data(&mut self, value: ::capnp::data::Reader<'_>) { + self.builder.get_pointer_field(0).set_data(value); + } + #[inline] + pub fn init_data(self, size: u32) -> ::capnp::data::Builder<'a> { + self.builder.get_pointer_field(0).init_data(size) + } + #[inline] + pub fn has_data(&self) -> bool { + !self.builder.get_pointer_field(0).is_null() + } + #[inline] + pub fn get_span(self) -> ::capnp::Result> { + ::capnp::traits::FromPointerBuilder::get_from_pointer( + self.builder.get_pointer_field(1), + ::core::option::Option::None, + ) + } + #[inline] + pub fn set_span( + &mut self, + value: crate::plugin_capnp::span::Reader<'_>, + ) -> ::capnp::Result<()> { + ::capnp::traits::SetPointerBuilder::set_pointer_builder( + self.builder.get_pointer_field(1), + value, + false, + ) + } + #[inline] + pub fn init_span(self) -> crate::plugin_capnp::span::Builder<'a> { + ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(1), 0) + } + #[inline] + pub fn has_span(&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 { + pub fn get_span(&self) -> crate::plugin_capnp::span::Pipeline { + ::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(1)) + } + } + mod _private { + use capnp::private::layout; + pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { + data: 0, + pointers: 2, + }; + pub const TYPE_ID: u64 = 0xe394_537e_5286_0449; + } +} + +pub mod call_input { + pub use self::Which::{PluginData, Value}; + + #[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 has_value(&self) -> bool { + if self.reader.get_data_field::(0) != 0 { + return false; + } + !self.reader.get_pointer_field(0).is_null() + } + #[inline] + pub fn has_plugin_data(&self) -> bool { + if self.reader.get_data_field::(0) != 1 { + 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(Value( + ::capnp::traits::FromPointerReader::get_from_pointer( + &self.reader.get_pointer_field(0), + ::core::option::Option::None, + ), + )), + 1 => ::core::result::Result::Ok(PluginData( + ::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_value( + &mut self, + value: crate::plugin_capnp::value::Reader<'_>, + ) -> ::capnp::Result<()> { + self.builder.set_data_field::(0, 0); + ::capnp::traits::SetPointerBuilder::set_pointer_builder( + self.builder.get_pointer_field(0), + value, + false, + ) + } + #[inline] + pub fn init_value(self) -> crate::plugin_capnp::value::Builder<'a> { + self.builder.set_data_field::(0, 0); + ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0) + } + #[inline] + pub fn has_value(&self) -> bool { + if self.builder.get_data_field::(0) != 0 { + return false; + } + !self.builder.get_pointer_field(0).is_null() + } + #[inline] + pub fn set_plugin_data( + &mut self, + value: crate::plugin_capnp::plugin_data::Reader<'_>, + ) -> ::capnp::Result<()> { + self.builder.set_data_field::(0, 1); + ::capnp::traits::SetPointerBuilder::set_pointer_builder( + self.builder.get_pointer_field(0), + value, + false, + ) + } + #[inline] + pub fn init_plugin_data(self) -> crate::plugin_capnp::plugin_data::Builder<'a> { + self.builder.set_data_field::(0, 1); + ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0) + } + #[inline] + pub fn has_plugin_data(&self) -> bool { + if self.builder.get_data_field::(0) != 1 { + 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(Value( + ::capnp::traits::FromPointerBuilder::get_from_pointer( + self.builder.get_pointer_field(0), + ::core::option::Option::None, + ), + )), + 1 => ::core::result::Result::Ok(PluginData( + ::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: 1, + pointers: 1, + }; + pub const TYPE_ID: u64 = 0xe0a9_a296_3cb2_e15f; + } + pub enum Which { + Value(A0), + PluginData(A1), + } + pub type WhichReader<'a> = Which< + ::capnp::Result>, + ::capnp::Result>, + >; + pub type WhichBuilder<'a> = Which< + ::capnp::Result>, + ::capnp::Result>, + >; +} + pub mod call_info { #[derive(Copy, Clone)] pub struct Owned(()); @@ -3265,6 +3818,7 @@ pub mod call_info { ::core::option::Option::None, ) } + #[inline] pub fn has_name(&self) -> bool { !self.reader.get_pointer_field(0).is_null() } @@ -3275,16 +3829,18 @@ pub mod call_info { ::core::option::Option::None, ) } + #[inline] pub fn has_call(&self) -> bool { !self.reader.get_pointer_field(1).is_null() } #[inline] - pub fn get_input(self) -> ::capnp::Result> { + pub fn get_input(self) -> ::capnp::Result> { ::capnp::traits::FromPointerReader::get_from_pointer( &self.reader.get_pointer_field(2), ::core::option::Option::None, ) } + #[inline] pub fn has_input(&self) -> bool { !self.reader.get_pointer_field(2).is_null() } @@ -3374,6 +3930,7 @@ pub mod call_info { pub fn init_name(self, size: u32) -> ::capnp::text::Builder<'a> { self.builder.get_pointer_field(0).init_text(size) } + #[inline] pub fn has_name(&self) -> bool { !self.builder.get_pointer_field(0).is_null() } @@ -3399,11 +3956,12 @@ pub mod call_info { pub fn init_call(self) -> crate::plugin_capnp::evaluated_call::Builder<'a> { ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(1), 0) } + #[inline] pub fn has_call(&self) -> bool { !self.builder.get_pointer_field(1).is_null() } #[inline] - pub fn get_input(self) -> ::capnp::Result> { + pub fn get_input(self) -> ::capnp::Result> { ::capnp::traits::FromPointerBuilder::get_from_pointer( self.builder.get_pointer_field(2), ::core::option::Option::None, @@ -3412,7 +3970,7 @@ pub mod call_info { #[inline] pub fn set_input( &mut self, - value: crate::plugin_capnp::value::Reader<'_>, + value: crate::plugin_capnp::call_input::Reader<'_>, ) -> ::capnp::Result<()> { ::capnp::traits::SetPointerBuilder::set_pointer_builder( self.builder.get_pointer_field(2), @@ -3421,9 +3979,10 @@ pub mod call_info { ) } #[inline] - pub fn init_input(self) -> crate::plugin_capnp::value::Builder<'a> { + pub fn init_input(self) -> crate::plugin_capnp::call_input::Builder<'a> { ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(2), 0) } + #[inline] pub fn has_input(&self) -> bool { !self.builder.get_pointer_field(2).is_null() } @@ -3443,7 +4002,7 @@ pub mod call_info { 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 { + pub fn get_input(&self) -> crate::plugin_capnp::call_input::Pipeline { ::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(2)) } } @@ -3458,7 +4017,7 @@ pub mod call_info { } pub mod plugin_call { - pub use self::Which::{CallInfo, Signature}; + pub use self::Which::{CallInfo, CollapseCustomValue, Signature}; #[derive(Copy, Clone)] pub struct Owned(()); @@ -3523,6 +4082,7 @@ pub mod plugin_call { pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> { self.reader.total_size() } + #[inline] pub fn has_call_info(&self) -> bool { if self.reader.get_data_field::(0) != 1 { return false; @@ -3530,6 +4090,13 @@ pub mod plugin_call { !self.reader.get_pointer_field(0).is_null() } #[inline] + pub fn has_collapse_custom_value(&self) -> bool { + if self.reader.get_data_field::(0) != 2 { + 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(Signature(())), @@ -3539,6 +4106,12 @@ pub mod plugin_call { ::core::option::Option::None, ), )), + 2 => ::core::result::Result::Ok(CollapseCustomValue( + ::capnp::traits::FromPointerReader::get_from_pointer( + &self.reader.get_pointer_field(0), + ::core::option::Option::None, + ), + )), x => ::core::result::Result::Err(::capnp::NotInSchema(x)), } } @@ -3634,6 +4207,7 @@ pub mod plugin_call { self.builder.set_data_field::(0, 1); ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0) } + #[inline] pub fn has_call_info(&self) -> bool { if self.builder.get_data_field::(0) != 1 { return false; @@ -3641,6 +4215,30 @@ pub mod plugin_call { !self.builder.get_pointer_field(0).is_null() } #[inline] + pub fn set_collapse_custom_value( + &mut self, + value: crate::plugin_capnp::plugin_data::Reader<'_>, + ) -> ::capnp::Result<()> { + self.builder.set_data_field::(0, 2); + ::capnp::traits::SetPointerBuilder::set_pointer_builder( + self.builder.get_pointer_field(0), + value, + false, + ) + } + #[inline] + pub fn init_collapse_custom_value(self) -> crate::plugin_capnp::plugin_data::Builder<'a> { + self.builder.set_data_field::(0, 2); + ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0) + } + #[inline] + pub fn has_collapse_custom_value(&self) -> bool { + if self.builder.get_data_field::(0) != 2 { + 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(Signature(())), @@ -3650,6 +4248,12 @@ pub mod plugin_call { ::core::option::Option::None, ), )), + 2 => ::core::result::Result::Ok(CollapseCustomValue( + ::capnp::traits::FromPointerBuilder::get_from_pointer( + self.builder.get_pointer_field(0), + ::core::option::Option::None, + ), + )), x => ::core::result::Result::Err(::capnp::NotInSchema(x)), } } @@ -3674,16 +4278,23 @@ pub mod plugin_call { }; pub const TYPE_ID: u64 = 0xde86_64b2_7f80_4db1; } - pub enum Which { + pub enum Which { Signature(()), CallInfo(A0), + CollapseCustomValue(A1), } - pub type WhichReader<'a> = Which<::capnp::Result>>; - pub type WhichBuilder<'a> = Which<::capnp::Result>>; + pub type WhichReader<'a> = Which< + ::capnp::Result>, + ::capnp::Result>, + >; + pub type WhichBuilder<'a> = Which< + ::capnp::Result>, + ::capnp::Result>, + >; } pub mod plugin_response { - pub use self::Which::{Error, Signature, Value}; + pub use self::Which::{Error, PluginData, Signature, Value}; #[derive(Copy, Clone)] pub struct Owned(()); @@ -3748,18 +4359,21 @@ pub mod plugin_response { pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> { self.reader.total_size() } + #[inline] pub fn has_error(&self) -> bool { if self.reader.get_data_field::(0) != 0 { return false; } !self.reader.get_pointer_field(0).is_null() } + #[inline] pub fn has_signature(&self) -> bool { if self.reader.get_data_field::(0) != 1 { return false; } !self.reader.get_pointer_field(0).is_null() } + #[inline] pub fn has_value(&self) -> bool { if self.reader.get_data_field::(0) != 2 { return false; @@ -3767,6 +4381,13 @@ pub mod plugin_response { !self.reader.get_pointer_field(0).is_null() } #[inline] + pub fn has_plugin_data(&self) -> bool { + if self.reader.get_data_field::(0) != 3 { + 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(Error( @@ -3787,6 +4408,12 @@ pub mod plugin_response { ::core::option::Option::None, ), )), + 3 => ::core::result::Result::Ok(PluginData( + ::capnp::traits::FromPointerReader::get_from_pointer( + &self.reader.get_pointer_field(0), + ::core::option::Option::None, + ), + )), x => ::core::result::Result::Err(::capnp::NotInSchema(x)), } } @@ -3878,6 +4505,7 @@ pub mod plugin_response { self.builder.set_data_field::(0, 0); ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0) } + #[inline] pub fn has_error(&self) -> bool { if self.builder.get_data_field::(0) != 0 { return false; @@ -3907,6 +4535,7 @@ pub mod plugin_response { size, ) } + #[inline] pub fn has_signature(&self) -> bool { if self.builder.get_data_field::(0) != 1 { return false; @@ -3930,6 +4559,7 @@ pub mod plugin_response { self.builder.set_data_field::(0, 2); ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0) } + #[inline] pub fn has_value(&self) -> bool { if self.builder.get_data_field::(0) != 2 { return false; @@ -3937,6 +4567,32 @@ pub mod plugin_response { !self.builder.get_pointer_field(0).is_null() } #[inline] + pub fn set_plugin_data( + &mut self, + value: crate::plugin_capnp::plugin_response::plugin_data_response::Reader<'_>, + ) -> ::capnp::Result<()> { + self.builder.set_data_field::(0, 3); + ::capnp::traits::SetPointerBuilder::set_pointer_builder( + self.builder.get_pointer_field(0), + value, + false, + ) + } + #[inline] + pub fn init_plugin_data( + self, + ) -> crate::plugin_capnp::plugin_response::plugin_data_response::Builder<'a> { + self.builder.set_data_field::(0, 3); + ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0) + } + #[inline] + pub fn has_plugin_data(&self) -> bool { + if self.builder.get_data_field::(0) != 3 { + 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(Error( @@ -3957,6 +4613,12 @@ pub mod plugin_response { ::core::option::Option::None, ), )), + 3 => ::core::result::Result::Ok(PluginData( + ::capnp::traits::FromPointerBuilder::get_from_pointer( + self.builder.get_pointer_field(0), + ::core::option::Option::None, + ), + )), x => ::core::result::Result::Err(::capnp::NotInSchema(x)), } } @@ -3981,21 +4643,258 @@ pub mod plugin_response { }; pub const TYPE_ID: u64 = 0xb9ba_b3c7_9388_b7db; } - pub enum Which { + pub enum Which { Error(A0), Signature(A1), Value(A2), + PluginData(A3), } pub type WhichReader<'a> = Which< ::capnp::Result>, ::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::signature::Owned>>, ::capnp::Result>, + ::capnp::Result>, >; pub type WhichBuilder<'a> = Which< ::capnp::Result>, ::capnp::Result<::capnp::struct_list::Builder<'a, crate::plugin_capnp::signature::Owned>>, ::capnp::Result>, + ::capnp::Result>, >; + + pub mod plugin_data_response { + #[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_name(self) -> ::capnp::Result<::capnp::text::Reader<'a>> { + ::capnp::traits::FromPointerReader::get_from_pointer( + &self.reader.get_pointer_field(0), + ::core::option::Option::None, + ) + } + #[inline] + pub fn has_name(&self) -> bool { + !self.reader.get_pointer_field(0).is_null() + } + #[inline] + pub fn get_data(self) -> ::capnp::Result> { + ::capnp::traits::FromPointerReader::get_from_pointer( + &self.reader.get_pointer_field(1), + ::core::option::Option::None, + ) + } + #[inline] + pub fn has_data(&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_name(self) -> ::capnp::Result<::capnp::text::Builder<'a>> { + ::capnp::traits::FromPointerBuilder::get_from_pointer( + self.builder.get_pointer_field(0), + ::core::option::Option::None, + ) + } + #[inline] + pub fn set_name(&mut self, value: ::capnp::text::Reader<'_>) { + self.builder.get_pointer_field(0).set_text(value); + } + #[inline] + pub fn init_name(self, size: u32) -> ::capnp::text::Builder<'a> { + self.builder.get_pointer_field(0).init_text(size) + } + #[inline] + pub fn has_name(&self) -> bool { + !self.builder.get_pointer_field(0).is_null() + } + #[inline] + pub fn get_data( + self, + ) -> ::capnp::Result> { + ::capnp::traits::FromPointerBuilder::get_from_pointer( + self.builder.get_pointer_field(1), + ::core::option::Option::None, + ) + } + #[inline] + pub fn set_data( + &mut self, + value: crate::plugin_capnp::plugin_data::Reader<'_>, + ) -> ::capnp::Result<()> { + ::capnp::traits::SetPointerBuilder::set_pointer_builder( + self.builder.get_pointer_field(1), + value, + false, + ) + } + #[inline] + pub fn init_data(self) -> crate::plugin_capnp::plugin_data::Builder<'a> { + ::capnp::traits::FromPointerBuilder::init_pointer( + self.builder.get_pointer_field(1), + 0, + ) + } + #[inline] + pub fn has_data(&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 { + pub fn get_data(&self) -> crate::plugin_capnp::plugin_data::Pipeline { + ::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(1)) + } + } + mod _private { + use capnp::private::layout; + pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { + data: 0, + pointers: 2, + }; + pub const TYPE_ID: u64 = 0xa0e9_dd1c_d7fe_17a7; + } + } } pub mod labeled_error { @@ -4069,6 +4968,7 @@ pub mod labeled_error { ::core::option::Option::None, ) } + #[inline] pub fn has_label(&self) -> bool { !self.reader.get_pointer_field(0).is_null() } @@ -4079,6 +4979,7 @@ pub mod labeled_error { ::core::option::Option::None, ) } + #[inline] pub fn has_msg(&self) -> bool { !self.reader.get_pointer_field(1).is_null() } @@ -4089,6 +4990,7 @@ pub mod labeled_error { ::core::option::Option::None, ) } + #[inline] pub fn has_span(&self) -> bool { !self.reader.get_pointer_field(2).is_null() } @@ -4178,6 +5080,7 @@ pub mod labeled_error { pub fn init_label(self, size: u32) -> ::capnp::text::Builder<'a> { self.builder.get_pointer_field(0).init_text(size) } + #[inline] pub fn has_label(&self) -> bool { !self.builder.get_pointer_field(0).is_null() } @@ -4196,6 +5099,7 @@ pub mod labeled_error { pub fn init_msg(self, size: u32) -> ::capnp::text::Builder<'a> { self.builder.get_pointer_field(1).init_text(size) } + #[inline] pub fn has_msg(&self) -> bool { !self.builder.get_pointer_field(1).is_null() } @@ -4221,6 +5125,7 @@ pub mod labeled_error { pub fn init_span(self) -> crate::plugin_capnp::span::Builder<'a> { ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(2), 0) } + #[inline] pub fn has_span(&self) -> bool { !self.builder.get_pointer_field(2).is_null() } diff --git a/crates/nu-plugin/src/protocol/mod.rs b/crates/nu-plugin/src/protocol/mod.rs index 4eebcd947..e80e13e7c 100644 --- a/crates/nu-plugin/src/protocol/mod.rs +++ b/crates/nu-plugin/src/protocol/mod.rs @@ -1,21 +1,32 @@ mod evaluated_call; +mod plugin_custom_value; +mod plugin_data; pub use evaluated_call::EvaluatedCall; use nu_protocol::{ShellError, Signature, Span, Value}; +pub use plugin_custom_value::PluginCustomValue; +pub use plugin_data::PluginData; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] pub struct CallInfo { pub name: String, pub call: EvaluatedCall, - pub input: Value, + pub input: CallInput, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub enum CallInput { + Value(Value), + Data(PluginData), } // Information sent to the plugin #[derive(Serialize, Deserialize, Debug)] pub enum PluginCall { Signature, - CallInfo(Box), + CallInfo(CallInfo), + CollapseCustomValue(PluginData), } #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] @@ -88,4 +99,5 @@ pub enum PluginResponse { Error(LabeledError), Signature(Vec), Value(Box), + PluginData(String, PluginData), } diff --git a/crates/nu-plugin/src/protocol/plugin_custom_value.rs b/crates/nu-plugin/src/protocol/plugin_custom_value.rs new file mode 100644 index 000000000..a85be449e --- /dev/null +++ b/crates/nu-plugin/src/protocol/plugin_custom_value.rs @@ -0,0 +1,126 @@ +use std::path::PathBuf; + +use nu_protocol::{CustomValue, ShellError, Value}; +use serde::Serialize; + +use crate::{ + plugin::{call_plugin, create_command}, + EncodingType, +}; + +use super::{PluginCall, PluginData, PluginResponse}; + +/// An opaque container for a custom value that is handled fully by a plugin +/// +/// This is constructed by the main nushell engine when it receives [`PluginResponse::PluginData`] +/// it stores that data as well as metadata related to the plugin to be able to call the plugin +/// later. +/// Since the data in it is opaque to the engine, there are only two final destinations for it: +/// either it will be sent back to the plugin that generated it across a pipeline, or it will be +/// sent to the plugin with a request to collapse it into a base value +#[derive(Clone, Debug, Serialize)] +pub struct PluginCustomValue { + /// The name of the custom value as defined by the plugin + pub name: String, + pub data: Vec, + pub filename: PathBuf, + + // PluginCustomValue must implement Serialize because all CustomValues must implement Serialize + // However, the main place where values are serialized and deserialized is when they are being + // sent between plugins and nushell's main engine. PluginCustomValue is never meant to be sent + // between that boundary + #[serde(skip)] + pub shell: Option, + #[serde(skip)] + pub encoding: EncodingType, + #[serde(skip)] + pub source: String, +} + +impl CustomValue for PluginCustomValue { + fn clone_value(&self, span: nu_protocol::Span) -> nu_protocol::Value { + Value::CustomValue { + val: Box::new(self.clone()), + span, + } + } + + fn value_string(&self) -> String { + self.name.clone() + } + + fn to_base_value( + &self, + span: nu_protocol::Span, + ) -> Result { + let mut plugin_cmd = create_command(&self.filename, &self.shell); + + let mut child = plugin_cmd.spawn().map_err(|err| { + ShellError::GenericError( + format!( + "Unable to spawn plugin for {} to get base value", + self.source + ), + format!("{}", err), + Some(span), + None, + Vec::new(), + ) + })?; + + let plugin_call = PluginCall::CollapseCustomValue(PluginData { + data: self.data.clone(), + span, + }); + + let response = call_plugin(&mut child, plugin_call, &self.encoding, span).map_err(|err| { + ShellError::GenericError( + format!( + "Unable to decode call for {} to get base value", + self.source + ), + format!("{}", err), + Some(span), + None, + Vec::new(), + ) + }); + + let value = match response { + Ok(PluginResponse::Value(value)) => Ok(*value), + Ok(PluginResponse::PluginData(..)) => Err(ShellError::GenericError( + "Plugin misbehaving".into(), + "Plugin returned custom data as a response to a collapse call".into(), + Some(span), + None, + Vec::new(), + )), + Ok(PluginResponse::Error(err)) => Err(err.into()), + Ok(PluginResponse::Signature(..)) => Err(ShellError::GenericError( + "Plugin missing value".into(), + "Received a signature from plugin instead of value".into(), + Some(span), + None, + Vec::new(), + )), + Err(err) => Err(err), + }; + + // We need to call .wait() on the child, or we'll risk summoning the zombie horde + let _ = child.wait(); + + value + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn typetag_name(&self) -> &'static str { + "PluginCustomValue" + } + + fn typetag_deserialize(&self) { + unimplemented!("typetag_deserialize") + } +} diff --git a/crates/nu-plugin/src/protocol/plugin_data.rs b/crates/nu-plugin/src/protocol/plugin_data.rs new file mode 100644 index 000000000..6b6b2ccdb --- /dev/null +++ b/crates/nu-plugin/src/protocol/plugin_data.rs @@ -0,0 +1,8 @@ +use nu_protocol::Span; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct PluginData { + pub data: Vec, + pub span: Span, +} diff --git a/crates/nu-plugin/src/serializers/capnp/call_input.rs b/crates/nu-plugin/src/serializers/capnp/call_input.rs new file mode 100644 index 000000000..bc20a0d9b --- /dev/null +++ b/crates/nu-plugin/src/serializers/capnp/call_input.rs @@ -0,0 +1,113 @@ +use super::{plugin_data, value}; +use crate::{plugin_capnp::call_input, protocol::CallInput}; +use nu_protocol::{ShellError, Span}; + +pub(crate) fn serialize_call_input(call_input: &CallInput, builder: call_input::Builder) { + match call_input { + CallInput::Value(value) => { + value::serialize_value(value, builder.init_value()); + } + CallInput::Data(plugin_data) => { + let builder = builder.init_plugin_data(); + + plugin_data::serialize_plugin_data(plugin_data, builder); + } + }; +} + +pub(crate) fn deserialize_call_input(reader: call_input::Reader) -> Result { + match reader.which() { + Err(capnp::NotInSchema(_)) => Err(ShellError::PluginFailedToDecode( + "value not in schema".into(), + )), + Ok(call_input::Value(value_reader)) => { + let value_reader = + value_reader.map_err(|e| ShellError::PluginFailedToDecode(e.to_string()))?; + + let span_reader = value_reader + .get_span() + .map_err(|e| ShellError::PluginFailedToDecode(e.to_string()))?; + + let span = Span { + start: span_reader.get_start() as usize, + end: span_reader.get_end() as usize, + }; + + Ok(CallInput::Value(value::deserialize_value( + value_reader, + span, + )?)) + } + Ok(call_input::PluginData(plugin_data_reader)) => { + let plugin_data_reader = + plugin_data_reader.map_err(|e| ShellError::PluginFailedToDecode(e.to_string()))?; + + let plugin_data = plugin_data::deserialize_plugin_data(plugin_data_reader)?; + + Ok(CallInput::Data(plugin_data)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::protocol::{CallInput, PluginData}; + use capnp::serialize; + use nu_protocol::{Span, Value}; + + pub fn write_buffer( + call_input: &CallInput, + writer: &mut impl std::io::Write, + ) -> Result<(), ShellError> { + let mut message = ::capnp::message::Builder::new_default(); + + let mut builder = message.init_root::(); + + serialize_call_input(call_input, builder.reborrow()); + + serialize::write_message(writer, &message) + .map_err(|e| ShellError::PluginFailedToDecode(e.to_string())) + } + + 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| ShellError::PluginFailedToDecode(e.to_string()))?; + + deserialize_call_input(reader.reborrow()) + } + + #[test] + fn callinput_value_round_trip() { + let call_input = CallInput::Value(Value::String { + val: "abc".to_string(), + span: Span { start: 1, end: 20 }, + }); + + let mut buffer: Vec = Vec::new(); + write_buffer(&call_input, &mut buffer).expect("unable to serialize message"); + let returned_call_input = + read_buffer(&mut buffer.as_slice()).expect("unable to deserialize message"); + + assert_eq!(call_input, returned_call_input) + } + + #[test] + fn callinput_data_round_trip() { + let call_input = CallInput::Data(PluginData { + data: vec![1, 2, 3, 4, 5, 6, 7], + span: Span { start: 1, end: 20 }, + }); + + let mut buffer: Vec = Vec::new(); + write_buffer(&call_input, &mut buffer).expect("unable to serialize message"); + let returned_call_input = + read_buffer(&mut buffer.as_slice()).expect("unable to deserialize message"); + + assert_eq!(call_input, returned_call_input) + } +} diff --git a/crates/nu-plugin/src/serializers/capnp/mod.rs b/crates/nu-plugin/src/serializers/capnp/mod.rs index 74707cd32..96bd87ddf 100644 --- a/crates/nu-plugin/src/serializers/capnp/mod.rs +++ b/crates/nu-plugin/src/serializers/capnp/mod.rs @@ -1,5 +1,7 @@ mod call; +mod call_input; mod plugin_call; +mod plugin_data; mod signature; mod value; @@ -7,7 +9,7 @@ use nu_protocol::ShellError; use crate::{plugin::PluginEncoder, protocol::PluginResponse}; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct CapnpSerializer; impl PluginEncoder for CapnpSerializer { diff --git a/crates/nu-plugin/src/serializers/capnp/plugin_call.rs b/crates/nu-plugin/src/serializers/capnp/plugin_call.rs index 3a4cd1e9a..8bdc14234 100644 --- a/crates/nu-plugin/src/serializers/capnp/plugin_call.rs +++ b/crates/nu-plugin/src/serializers/capnp/plugin_call.rs @@ -1,5 +1,5 @@ use super::signature::deserialize_signature; -use super::{call, signature, value}; +use super::{call, call_input, plugin_data, signature, value}; use crate::plugin_capnp::{plugin_call, plugin_response}; use crate::protocol::{CallInfo, LabeledError, PluginCall, PluginResponse}; use capnp::serialize; @@ -31,12 +31,17 @@ pub fn encode_call( .map_err(|e| ShellError::PluginFailedToEncode(e.to_string()))?; // Serializing the input value from the call info - let value_builder = call_info_builder + let call_input_builder = call_info_builder .reborrow() .get_input() .map_err(|e| ShellError::PluginFailedToEncode(e.to_string()))?; - value::serialize_value(&call_info.input, value_builder); + call_input::serialize_call_input(&call_info.input, call_input_builder); + } + PluginCall::CollapseCustomValue(plugin_data) => { + let builder = builder.init_collapse_custom_value(); + + plugin_data::serialize_plugin_data(plugin_data, builder); } }; @@ -74,13 +79,20 @@ pub fn decode_call(reader: &mut impl std::io::BufRead) -> Result { + let reader = reader.map_err(|e| ShellError::PluginFailedToDecode(e.to_string()))?; + + let plugin_data = plugin_data::deserialize_plugin_data(reader)?; + + Ok(PluginCall::CollapseCustomValue(plugin_data)) } } } @@ -118,6 +130,12 @@ pub fn encode_response( let value_builder = builder.reborrow().init_value(); value::serialize_value(val, value_builder); } + PluginResponse::PluginData(name, plugin_data) => { + let mut plugin_data_builder = builder.reborrow().init_plugin_data(); + + plugin_data_builder.set_name(name); + plugin_data::serialize_plugin_data(plugin_data, plugin_data_builder.init_data()); + } }; serialize::write_message(writer, &message) @@ -196,13 +214,29 @@ pub fn decode_response(reader: &mut impl std::io::BufRead) -> Result { + let reader = reader.map_err(|e| ShellError::PluginFailedToDecode(e.to_string()))?; + + let name = reader + .get_name() + .map_err(|e| ShellError::PluginFailedToDecode(e.to_string()))?; + + let plugin_data_reader = reader + .get_data() + .map_err(|e| ShellError::PluginFailedToDecode(e.to_string()))?; + let plugin_data = plugin_data::deserialize_plugin_data(plugin_data_reader)?; + + Ok(PluginResponse::PluginData(name.to_string(), plugin_data)) + } } } #[cfg(test)] mod tests { use super::*; - use crate::protocol::{EvaluatedCall, LabeledError, PluginCall, PluginResponse}; + use crate::protocol::{ + CallInput, EvaluatedCall, LabeledError, PluginCall, PluginData, PluginResponse, + }; use nu_protocol::{Signature, Span, Spanned, SyntaxShape, Value}; #[test] @@ -216,6 +250,7 @@ mod tests { match returned { PluginCall::Signature => {} PluginCall::CallInfo(_) => panic!("decoded into wrong value"), + PluginCall::CollapseCustomValue(_) => panic!("decoded into wrong value"), } } @@ -223,10 +258,10 @@ mod tests { fn callinfo_round_trip_callinfo() { let name = "test".to_string(); - let input = Value::Bool { + let input = CallInput::Value(Value::Bool { val: false, span: Span { start: 1, end: 20 }, - }; + }); let call = EvaluatedCall { head: Span { start: 0, end: 10 }, @@ -252,11 +287,18 @@ mod tests { )], }; - let plugin_call = PluginCall::CallInfo(Box::new(CallInfo { + let plugin_call = PluginCall::CallInfo(CallInfo { name: name.clone(), call: call.clone(), - input: input.clone(), - })); + // Avoiding having to implement Clone on CallInput just for tests + input: match &input { + CallInput::Value(value) => CallInput::Value(value.clone()), + CallInput::Data(plugin_data) => CallInput::Data(PluginData { + data: plugin_data.data.clone(), + span: plugin_data.span, + }), + }, + }); let mut buffer: Vec = Vec::new(); encode_call(&plugin_call, &mut buffer).expect("unable to serialize message"); @@ -266,7 +308,7 @@ mod tests { PluginCall::Signature => panic!("returned wrong call type"), PluginCall::CallInfo(call_info) => { assert_eq!(name, call_info.name); - assert_eq!(input, call_info.input); + assert_eq!(&input, &call_info.input); assert_eq!(call.head, call_info.call.head); assert_eq!(call.positional.len(), call_info.call.positional.len()); @@ -289,6 +331,31 @@ mod tests { } }); } + PluginCall::CollapseCustomValue(_) => panic!("returned wrong call type"), + } + } + + #[test] + fn callinfo_round_trip_collapsecustomvalue() { + let data = vec![1, 2, 3, 4, 5, 6, 7]; + let span = Span { start: 0, end: 20 }; + + let collapse_custom_value = PluginCall::CollapseCustomValue(PluginData { + data: data.clone(), + span, + }); + + let mut buffer: Vec = Vec::new(); + encode_call(&collapse_custom_value, &mut buffer).expect("unable to serialize message"); + let returned = decode_call(&mut buffer.as_slice()).expect("unable to deserialize message"); + + match returned { + PluginCall::Signature => panic!("returned wrong call type"), + PluginCall::CallInfo(_) => panic!("returned wrong call type"), + PluginCall::CollapseCustomValue(plugin_data) => { + assert_eq!(data, plugin_data.data); + assert_eq!(span, plugin_data.span); + } } } @@ -316,6 +383,7 @@ mod tests { match returned { PluginResponse::Error(_) => panic!("returned wrong call type"), PluginResponse::Value(_) => panic!("returned wrong call type"), + PluginResponse::PluginData(..) => panic!("returned wrong call type"), PluginResponse::Signature(returned_signature) => { assert!(returned_signature.len() == 1); assert_eq!(signature.name, returned_signature[0].name); @@ -366,12 +434,45 @@ mod tests { match returned { PluginResponse::Error(_) => panic!("returned wrong call type"), PluginResponse::Signature(_) => panic!("returned wrong call type"), + PluginResponse::PluginData(..) => panic!("returned wrong call type"), PluginResponse::Value(returned_value) => { assert_eq!(&value, returned_value.as_ref()) } } } + #[test] + fn response_round_trip_plugin_data() { + let name = "test".to_string(); + + let data = vec![1, 2, 3, 4, 5]; + let span = Span { start: 2, end: 30 }; + + let response = PluginResponse::PluginData( + name.clone(), + PluginData { + data: data.clone(), + span, + }, + ); + + let mut buffer: Vec = Vec::new(); + encode_response(&response, &mut buffer).expect("unable to serialize message"); + let returned = + decode_response(&mut buffer.as_slice()).expect("unable to deserialize message"); + + match returned { + PluginResponse::Error(_) => panic!("returned wrong call type"), + PluginResponse::Signature(_) => panic!("returned wrong call type"), + PluginResponse::Value(_) => panic!("returned wrong call type"), + PluginResponse::PluginData(returned_name, returned_plugin_data) => { + assert_eq!(name, returned_name); + assert_eq!(data, returned_plugin_data.data); + assert_eq!(span, returned_plugin_data.span); + } + } + } + #[test] fn response_round_trip_error() { let error = LabeledError { @@ -390,6 +491,7 @@ mod tests { PluginResponse::Error(msg) => assert_eq!(error, msg), PluginResponse::Signature(_) => panic!("returned wrong call type"), PluginResponse::Value(_) => panic!("returned wrong call type"), + PluginResponse::PluginData(..) => panic!("returned wrong call type"), } } @@ -411,6 +513,7 @@ mod tests { PluginResponse::Error(msg) => assert_eq!(error, msg), PluginResponse::Signature(_) => panic!("returned wrong call type"), PluginResponse::Value(_) => panic!("returned wrong call type"), + PluginResponse::PluginData(..) => panic!("returned wrong call type"), } } } diff --git a/crates/nu-plugin/src/serializers/capnp/plugin_data.rs b/crates/nu-plugin/src/serializers/capnp/plugin_data.rs new file mode 100644 index 000000000..5b007d365 --- /dev/null +++ b/crates/nu-plugin/src/serializers/capnp/plugin_data.rs @@ -0,0 +1,79 @@ +use crate::{plugin_capnp::plugin_data, protocol::PluginData}; +use nu_protocol::{ShellError, Span}; + +pub(crate) fn serialize_plugin_data(plugin_data: &PluginData, mut builder: plugin_data::Builder) { + builder.set_data(&plugin_data.data); + + let mut span_builder = builder.init_span(); + span_builder.set_start(plugin_data.span.start as u64); + span_builder.set_end(plugin_data.span.end as u64); +} + +pub(crate) fn deserialize_plugin_data( + reader: plugin_data::Reader, +) -> Result { + let data = reader + .get_data() + .map_err(|e| ShellError::PluginFailedToDecode(e.to_string()))?; + + let span_reader = reader + .get_span() + .map_err(|e| ShellError::PluginFailedToDecode(e.to_string()))?; + + let span = Span { + start: span_reader.get_start() as usize, + end: span_reader.get_end() as usize, + }; + + Ok(PluginData { + data: data.to_vec(), + span, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use capnp::serialize; + use nu_protocol::Span; + + pub fn write_buffer( + plugin_data: &PluginData, + writer: &mut impl std::io::Write, + ) -> Result<(), ShellError> { + let mut message = ::capnp::message::Builder::new_default(); + + let mut builder = message.init_root::(); + + serialize_plugin_data(plugin_data, builder.reborrow()); + + serialize::write_message(writer, &message) + .map_err(|e| ShellError::PluginFailedToDecode(e.to_string())) + } + + 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| ShellError::PluginFailedToDecode(e.to_string()))?; + + deserialize_plugin_data(reader.reborrow()) + } + + #[test] + fn plugin_data_round_trip() { + let plugin_data = PluginData { + data: vec![1, 2, 3, 4, 5, 6, 7], + span: Span { start: 1, end: 20 }, + }; + + let mut buffer: Vec = Vec::new(); + write_buffer(&plugin_data, &mut buffer).expect("unable to serialize message"); + let returned_plugin_data = + read_buffer(&mut buffer.as_slice()).expect("unable to deserialize message"); + + assert_eq!(plugin_data, returned_plugin_data) + } +} diff --git a/crates/nu-plugin/src/serializers/capnp/schema/plugin.capnp b/crates/nu-plugin/src/serializers/capnp/schema/plugin.capnp index e148d0957..483c3a349 100644 --- a/crates/nu-plugin/src/serializers/capnp/schema/plugin.capnp +++ b/crates/nu-plugin/src/serializers/capnp/schema/plugin.capnp @@ -121,10 +121,22 @@ struct EvaluatedCall { named @2 :Map(Text, Value); } +struct PluginData { + data @0 :Data; + span @1 :Span; +} + +struct CallInput { + union { + value @0 :Value; + pluginData @1 :PluginData; + } +} + struct CallInfo { name @0 :Text; call @1 :EvaluatedCall; - input @2 :Value; + input @2 :CallInput; } # Main communication structs with the plugin @@ -132,6 +144,7 @@ struct PluginCall { union { signature @0 :Void; callInfo @1 :CallInfo; + collapseCustomValue @2 :PluginData; } } @@ -140,6 +153,12 @@ struct PluginResponse { error @0 :LabeledError; signature @1 :List(Signature); value @2 :Value; + pluginData @3 :PluginDataResponse; + } + + struct PluginDataResponse { + name @0 :Text; + data @1 :PluginData; } } diff --git a/crates/nu-plugin/src/serializers/json.rs b/crates/nu-plugin/src/serializers/json.rs index 7d16cab30..87a43d050 100644 --- a/crates/nu-plugin/src/serializers/json.rs +++ b/crates/nu-plugin/src/serializers/json.rs @@ -2,7 +2,7 @@ use nu_protocol::ShellError; use crate::{plugin::PluginEncoder, protocol::PluginResponse}; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct JsonSerializer; impl PluginEncoder for JsonSerializer { @@ -44,7 +44,9 @@ impl PluginEncoder for JsonSerializer { #[cfg(test)] mod tests { use super::*; - use crate::protocol::{CallInfo, EvaluatedCall, LabeledError, PluginCall, PluginResponse}; + use crate::protocol::{ + CallInfo, CallInput, EvaluatedCall, LabeledError, PluginCall, PluginData, PluginResponse, + }; use nu_protocol::{Signature, Span, Spanned, SyntaxShape, Value}; #[test] @@ -63,6 +65,7 @@ mod tests { match returned { PluginCall::Signature => {} PluginCall::CallInfo(_) => panic!("decoded into wrong value"), + PluginCall::CollapseCustomValue(_) => panic!("decoded into wrong value"), } } @@ -99,11 +102,11 @@ mod tests { )], }; - let plugin_call = PluginCall::CallInfo(Box::new(CallInfo { + let plugin_call = PluginCall::CallInfo(CallInfo { name: name.clone(), call: call.clone(), - input: input.clone(), - })); + input: CallInput::Value(input.clone()), + }); let encoder = JsonSerializer {}; let mut buffer: Vec = Vec::new(); @@ -118,7 +121,7 @@ mod tests { PluginCall::Signature => panic!("returned wrong call type"), PluginCall::CallInfo(call_info) => { assert_eq!(name, call_info.name); - assert_eq!(input, call_info.input); + assert_eq!(CallInput::Value(input), call_info.input); assert_eq!(call.head, call_info.call.head); assert_eq!(call.positional.len(), call_info.call.positional.len()); @@ -141,6 +144,36 @@ mod tests { } }); } + PluginCall::CollapseCustomValue(_) => panic!("returned wrong call type"), + } + } + + #[test] + fn callinfo_round_trip_collapsecustomvalue() { + let data = vec![1, 2, 3, 4, 5, 6, 7]; + let span = Span { start: 0, end: 20 }; + + let collapse_custom_value = PluginCall::CollapseCustomValue(PluginData { + data: data.clone(), + span, + }); + + let encoder = JsonSerializer {}; + let mut buffer: Vec = Vec::new(); + encoder + .encode_call(&collapse_custom_value, &mut buffer) + .expect("unable to serialize message"); + let returned = encoder + .decode_call(&mut buffer.as_slice()) + .expect("unable to deserialize message"); + + match returned { + PluginCall::Signature => panic!("returned wrong call type"), + PluginCall::CallInfo(_) => panic!("returned wrong call type"), + PluginCall::CollapseCustomValue(plugin_data) => { + assert_eq!(data, plugin_data.data); + assert_eq!(span, plugin_data.span); + } } } @@ -172,6 +205,7 @@ mod tests { match returned { PluginResponse::Error(_) => panic!("returned wrong call type"), PluginResponse::Value(_) => panic!("returned wrong call type"), + PluginResponse::PluginData(..) => panic!("returned wrong call type"), PluginResponse::Signature(returned_signature) => { assert!(returned_signature.len() == 1); assert_eq!(signature.name, returned_signature[0].name); @@ -226,12 +260,49 @@ mod tests { match returned { PluginResponse::Error(_) => panic!("returned wrong call type"), PluginResponse::Signature(_) => panic!("returned wrong call type"), + PluginResponse::PluginData(..) => panic!("returned wrong call type"), PluginResponse::Value(returned_value) => { assert_eq!(&value, returned_value.as_ref()) } } } + #[test] + fn response_round_trip_plugin_data() { + let name = "test".to_string(); + + let data = vec![1, 2, 3, 4, 5]; + let span = Span { start: 2, end: 30 }; + + let response = PluginResponse::PluginData( + name.clone(), + PluginData { + data: data.clone(), + span, + }, + ); + + let encoder = JsonSerializer {}; + let mut buffer: Vec = Vec::new(); + encoder + .encode_response(&response, &mut buffer) + .expect("unable to serialize message"); + let returned = encoder + .decode_response(&mut buffer.as_slice()) + .expect("unable to deserialize message"); + + match returned { + PluginResponse::Error(_) => panic!("returned wrong call type"), + PluginResponse::Signature(_) => panic!("returned wrong call type"), + PluginResponse::Value(_) => panic!("returned wrong call type"), + PluginResponse::PluginData(returned_name, returned_plugin_data) => { + assert_eq!(name, returned_name); + assert_eq!(data, returned_plugin_data.data); + assert_eq!(span, returned_plugin_data.span); + } + } + } + #[test] fn response_round_trip_error() { let error = LabeledError { @@ -254,6 +325,7 @@ mod tests { PluginResponse::Error(msg) => assert_eq!(error, msg), PluginResponse::Signature(_) => panic!("returned wrong call type"), PluginResponse::Value(_) => panic!("returned wrong call type"), + PluginResponse::PluginData(..) => panic!("returned wrong call type"), } } @@ -279,6 +351,7 @@ mod tests { PluginResponse::Error(msg) => assert_eq!(error, msg), PluginResponse::Signature(_) => panic!("returned wrong call type"), PluginResponse::Value(_) => panic!("returned wrong call type"), + PluginResponse::PluginData(..) => panic!("returned wrong call type"), } } } diff --git a/crates/nu-plugin/src/serializers/mod.rs b/crates/nu-plugin/src/serializers/mod.rs index 937732b12..2c06d75f3 100644 --- a/crates/nu-plugin/src/serializers/mod.rs +++ b/crates/nu-plugin/src/serializers/mod.rs @@ -8,7 +8,7 @@ use crate::{ pub mod capnp; pub mod json; -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum EncodingType { Capnp(capnp::CapnpSerializer), Json(json::JsonSerializer), diff --git a/crates/nu_plugin_custom_values/Cargo.toml b/crates/nu_plugin_custom_values/Cargo.toml new file mode 100644 index 000000000..9f9ecf39a --- /dev/null +++ b/crates/nu_plugin_custom_values/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "nu_plugin_custom_values" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nu-plugin = { path = "../nu-plugin", version = "0.65.1" } +nu-protocol = { path = "../nu-protocol", version = "0.65.1", features = ["plugin"] } +serde = { version = "1.0", features = ["derive"] } +typetag = "0.1.8" diff --git a/crates/nu_plugin_custom_values/src/cool_custom_value.rs b/crates/nu_plugin_custom_values/src/cool_custom_value.rs new file mode 100644 index 000000000..6e125784c --- /dev/null +++ b/crates/nu_plugin_custom_values/src/cool_custom_value.rs @@ -0,0 +1,67 @@ +use nu_protocol::{CustomValue, ShellError, Span, Value}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CoolCustomValue { + pub(crate) cool: String, +} + +impl CoolCustomValue { + pub fn new(content: &str) -> Self { + Self { + cool: content.to_owned(), + } + } + + pub fn into_value(self, span: Span) -> Value { + Value::CustomValue { + val: Box::new(self), + span, + } + } + + pub fn try_from_value(value: &Value) -> Result { + match value { + Value::CustomValue { val, span } => match val.as_any().downcast_ref::() { + Some(cool) => Ok(cool.clone()), + None => Err(ShellError::CantConvert( + "cool".into(), + "non-cool".into(), + *span, + None, + )), + }, + x => Err(ShellError::CantConvert( + "cool".into(), + x.get_type().to_string(), + x.span()?, + None, + )), + } + } +} + +#[typetag::serde] +impl CustomValue for CoolCustomValue { + fn clone_value(&self, span: nu_protocol::Span) -> Value { + Value::CustomValue { + val: Box::new(self.clone()), + span, + } + } + + fn value_string(&self) -> String { + self.typetag_name().to_string() + } + + fn to_base_value(&self, span: nu_protocol::Span) -> Result { + Ok(Value::String { + val: format!("I used to be a custom value! My data was ({})", self.cool), + span, + }) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} diff --git a/crates/nu_plugin_custom_values/src/main.rs b/crates/nu_plugin_custom_values/src/main.rs new file mode 100644 index 000000000..77f4c5e46 --- /dev/null +++ b/crates/nu_plugin_custom_values/src/main.rs @@ -0,0 +1,78 @@ +mod cool_custom_value; +mod second_custom_value; + +use cool_custom_value::CoolCustomValue; +use nu_plugin::{serve_plugin, CapnpSerializer, Plugin}; +use nu_plugin::{EvaluatedCall, LabeledError}; +use nu_protocol::{Category, ShellError, Signature, Value}; +use second_custom_value::SecondCustomValue; + +struct CustomValuePlugin; + +impl Plugin for CustomValuePlugin { + fn signature(&self) -> Vec { + vec![ + Signature::build("custom-value generate") + .usage("Signature for a plugin that generates a custom value") + .category(Category::Experimental), + Signature::build("custom-value generate2") + .usage("Signature for a plugin that generates a different custom value") + .category(Category::Experimental), + Signature::build("custom-value update") + .usage("Signature for a plugin that updates a custom value") + .category(Category::Experimental), + ] + } + + fn run( + &mut self, + name: &str, + call: &EvaluatedCall, + input: &Value, + ) -> Result { + match name { + "custom-value generate" => self.generate(call, input), + "custom-value generate2" => self.generate2(call, input), + "custom-value update" => self.update(call, input), + _ => Err(LabeledError { + label: "Plugin call with wrong name signature".into(), + msg: "the signature used to call the plugin does not match any name in the plugin signature vector".into(), + span: Some(call.head), + }), + } + } +} + +impl CustomValuePlugin { + fn generate(&mut self, call: &EvaluatedCall, _input: &Value) -> Result { + Ok(CoolCustomValue::new("abc").into_value(call.head)) + } + + fn generate2(&mut self, call: &EvaluatedCall, _input: &Value) -> Result { + Ok(SecondCustomValue::new("xyz").into_value(call.head)) + } + + fn update(&mut self, call: &EvaluatedCall, input: &Value) -> Result { + if let Ok(mut value) = CoolCustomValue::try_from_value(input) { + value.cool += "xyz"; + return Ok(value.into_value(call.head)); + } + + if let Ok(mut value) = SecondCustomValue::try_from_value(input) { + value.something += "abc"; + return Ok(value.into_value(call.head)); + } + + Err(ShellError::CantConvert( + "cool or second".into(), + "non-cool and non-second".into(), + call.head, + None, + ) + .into()) + } +} + +fn main() { + serve_plugin(&mut CustomValuePlugin, CapnpSerializer {}) +} diff --git a/crates/nu_plugin_custom_values/src/second_custom_value.rs b/crates/nu_plugin_custom_values/src/second_custom_value.rs new file mode 100644 index 000000000..17e5b3c26 --- /dev/null +++ b/crates/nu_plugin_custom_values/src/second_custom_value.rs @@ -0,0 +1,70 @@ +use nu_protocol::{CustomValue, ShellError, Span, Value}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SecondCustomValue { + pub(crate) something: String, +} + +impl SecondCustomValue { + pub fn new(content: &str) -> Self { + Self { + something: content.to_owned(), + } + } + + pub fn into_value(self, span: Span) -> Value { + Value::CustomValue { + val: Box::new(self), + span, + } + } + + pub fn try_from_value(value: &Value) -> Result { + match value { + Value::CustomValue { val, span } => match val.as_any().downcast_ref::() { + Some(value) => Ok(value.clone()), + None => Err(ShellError::CantConvert( + "cool".into(), + "non-cool".into(), + *span, + None, + )), + }, + x => Err(ShellError::CantConvert( + "cool".into(), + x.get_type().to_string(), + x.span()?, + None, + )), + } + } +} + +#[typetag::serde] +impl CustomValue for SecondCustomValue { + fn clone_value(&self, span: nu_protocol::Span) -> Value { + Value::CustomValue { + val: Box::new(self.clone()), + span, + } + } + + fn value_string(&self) -> String { + self.typetag_name().to_string() + } + + fn to_base_value(&self, span: nu_protocol::Span) -> Result { + Ok(Value::String { + val: format!( + "I used to be a DIFFERENT custom value! ({})", + self.something + ), + span, + }) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} diff --git a/tests/plugins/custom_values.rs b/tests/plugins/custom_values.rs new file mode 100644 index 000000000..f7bc946f2 --- /dev/null +++ b/tests/plugins/custom_values.rs @@ -0,0 +1,84 @@ +use nu_test_support::nu_with_plugins; + +#[test] +fn can_get_custom_value_from_plugin_and_instantly_collapse_it() { + let actual = nu_with_plugins!( + cwd: "tests", + plugin: ("capnp", "nu_plugin_custom_values"), + "custom-value generate" + ); + + assert_eq!(actual.out, "I used to be a custom value! My data was (abc)"); +} + +#[test] +fn can_get_custom_value_from_plugin_and_pass_it_over() { + let actual = nu_with_plugins!( + cwd: "tests", + plugin: ("capnp", "nu_plugin_custom_values"), + "custom-value generate | custom-value update" + ); + + assert_eq!( + actual.out, + "I used to be a custom value! My data was (abcxyz)" + ); +} + +#[test] +fn can_generate_and_updated_multiple_types_of_custom_values() { + let actual = nu_with_plugins!( + cwd: "tests", + plugin: ("capnp", "nu_plugin_custom_values"), + "custom-value generate2 | custom-value update" + ); + + assert_eq!( + actual.out, + "I used to be a DIFFERENT custom value! (xyzabc)" + ); +} + +#[test] +fn can_get_describe_plugin_custom_values() { + let actual = nu_with_plugins!( + cwd: "tests", + plugin: ("capnp", "nu_plugin_custom_values"), + "custom-value generate | describe" + ); + + assert_eq!(actual.out, "CoolCustomValue"); +} + +// There are currently no custom values defined by the engine that aren't hidden behind an extra +// feature, both database and dataframes are hidden behind --features=extra so we need to guard +// this test +#[cfg(feature = "database")] +#[test] +fn fails_if_passing_engine_custom_values_to_plugins() { + let actual = nu_with_plugins!( + cwd: "tests/fixtures/formats", + plugin: ("capnp", "nu_plugin_custom_values"), + "open-db sample.db | custom-value update" + ); + + assert!(actual + .err + .contains("Plugin custom-value update can not handle the custom value SQLiteDatabase")); +} + +#[test] +fn fails_if_passing_custom_values_across_plugins() { + let actual = nu_with_plugins!( + cwd: "tests", + plugins: [ + ("capnp", "nu_plugin_custom_values"), + ("json", "nu_plugin_inc") + ], + "custom-value generate | inc --major" + ); + + assert!(actual + .err + .contains("Plugin inc can not handle the custom value CoolCustomValue")); +} diff --git a/tests/plugins/mod.rs b/tests/plugins/mod.rs index e3d59b220..78bdd59ac 100644 --- a/tests/plugins/mod.rs +++ b/tests/plugins/mod.rs @@ -1 +1,2 @@ mod core_inc; +mod custom_values;