mirror of
https://github.com/nushell/nushell.git
synced 2025-08-18 17:38:30 +02:00
Add CustomValue support to plugins (#6070)
* Skeleton implementation Lots and lots of TODOs * Bootstrap simple CustomValue plugin support test * Create nu_plugin_custom_value * Skeleton for nu_plugin_custom_values * Return a custom value from plugin * Encode CustomValues from plugin calls as PluginResponse::PluginData * Add new PluginCall variant CollapseCustomValue * Handle CollapseCustomValue plugin calls * Add CallInput::Data variant to CallInfo inputs * Handle CallInfo with CallInput::Data plugin calls * Send CallInput::Data if Value is PluginCustomValue from plugin calls * Remove unnecessary boxing of plugins CallInfo * Add fields needed to collapse PluginCustomValue to it * Document PluginCustomValue and its purpose * Impl collapsing using plugin calls in PluginCustomValue::to_base_value * Implement proper typetag based deserialization for CoolCustomValue * Test demonstrating that passing back a custom value to plugin works * Added a failing test for describing plugin CustomValues * Support describe for PluginCustomValues - Add name to PluginResponse::PluginData - Also turn it into a struct for clarity - Add name to PluginCustomValue - Return name field from PluginCustomValue * Demonstrate that plugins can create and handle multiple CustomValues * Add bincode to nu-plugin dependencies This is for demonstration purposes, any schemaless binary seralization format will work. I picked bincode since it's the most popular for Rust but there are defintely better options out there for this usecase * serde_json::Value -> Vec<u8> * Update capnp schema for new CallInfo.input field * Move call_input capnp serialization and deserialization into new file * Deserialize Value's span from Value itself instead of passing call.head I am not sure if this was correct and I am breaking it or if it was a bug, I don't fully understand how nu creates and uses Spans. What should reuse spans and what should recreate new ones? But yeah it felt weird that the Value's Span was being ignored since in the json serializer just uses the Value's Span * Add call_info value round trip test * Add capnp CallInput::Data serialization and deserialization support * Add CallInfo::CollapseCustomValue to capnp schema * Add capnp PluginCall::CollapseCustomValue serialization and deserialization support * Add PluginResponse::PluginData to capnp schema * Add capnp PluginResponse::PluginData serialization and deserialization support * Switch plugins::custom_values tests to capnp Both json and capnp would work now! Sadly I can't choose both at the same time :( * Add missing JsonSerializer round trip tests * Handle plugin returning PluginData as a response to CollapseCustomValue * Refactor plugin calling into a reusable function Many less levels of indentation now! * Export PluginData from nu_plugin So plugins can create their very own serve_plugin with whatever CustomValue behavior they may desire * Error if CustomValue cannot be handled by Plugin
This commit is contained in:
113
crates/nu-plugin/src/serializers/capnp/call_input.rs
Normal file
113
crates/nu-plugin/src/serializers/capnp/call_input.rs
Normal file
@@ -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<CallInput, ShellError> {
|
||||
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::<call_input::Builder>();
|
||||
|
||||
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<CallInput, ShellError> {
|
||||
let message_reader =
|
||||
serialize::read_message(reader, ::capnp::message::ReaderOptions::new()).unwrap();
|
||||
|
||||
let reader = message_reader
|
||||
.get_root::<call_input::Reader>()
|
||||
.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<u8> = 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<u8> = 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)
|
||||
}
|
||||
}
|
@@ -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 {
|
||||
|
@@ -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<PluginCall, She
|
||||
.get_input()
|
||||
.map_err(|e| ShellError::PluginFailedToDecode(e.to_string()))?;
|
||||
|
||||
let input = value::deserialize_value(input_reader, call.head)?;
|
||||
let input = call_input::deserialize_call_input(input_reader)?;
|
||||
|
||||
Ok(PluginCall::CallInfo(Box::new(CallInfo {
|
||||
Ok(PluginCall::CallInfo(CallInfo {
|
||||
name: name.to_string(),
|
||||
call,
|
||||
input,
|
||||
})))
|
||||
}))
|
||||
}
|
||||
Ok(plugin_call::CollapseCustomValue(reader)) => {
|
||||
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<PluginRespo
|
||||
|
||||
Ok(PluginResponse::Value(Box::new(val)))
|
||||
}
|
||||
Ok(plugin_response::PluginData(reader)) => {
|
||||
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<u8> = 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<u8> = 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<u8> = 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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
79
crates/nu-plugin/src/serializers/capnp/plugin_data.rs
Normal file
79
crates/nu-plugin/src/serializers/capnp/plugin_data.rs
Normal file
@@ -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<PluginData, ShellError> {
|
||||
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::<plugin_data::Builder>();
|
||||
|
||||
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<PluginData, ShellError> {
|
||||
let message_reader =
|
||||
serialize::read_message(reader, ::capnp::message::ReaderOptions::new()).unwrap();
|
||||
|
||||
let reader = message_reader
|
||||
.get_root::<plugin_data::Reader>()
|
||||
.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<u8> = 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)
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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<u8> = 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<u8> = 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<u8> = 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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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),
|
||||
|
Reference in New Issue
Block a user