From 382a09e4134b010382397eafdf23ccc90f7a6912 Mon Sep 17 00:00:00 2001 From: Jack Wright Date: Tue, 30 Jul 2024 21:13:50 -0700 Subject: [PATCH] Make pipeline metadata available to plugins --- crates/nu-plugin-core/src/interface/mod.rs | 40 +++++++---- crates/nu-plugin-core/src/interface/tests.rs | 71 +++++++++++++++---- .../nu-plugin-core/src/serializers/tests.rs | 24 +++++-- crates/nu-plugin-protocol/src/lib.rs | 28 +++++--- crates/nu-protocol/src/pipeline/metadata.rs | 6 +- .../nu_plugin_nu_example.nu | 5 +- crates/nu_plugin_stress_internals/src/main.rs | 2 +- 7 files changed, 128 insertions(+), 48 deletions(-) diff --git a/crates/nu-plugin-core/src/interface/mod.rs b/crates/nu-plugin-core/src/interface/mod.rs index 89aa2b15e5..065f43ee5e 100644 --- a/crates/nu-plugin-core/src/interface/mod.rs +++ b/crates/nu-plugin-core/src/interface/mod.rs @@ -174,16 +174,19 @@ pub trait InterfaceManager { ) -> Result { self.prepare_pipeline_data(match header { PipelineDataHeader::Empty => PipelineData::Empty, - PipelineDataHeader::Value(value) => PipelineData::Value(value, None), - PipelineDataHeader::ListStream(info) => { + PipelineDataHeader::Value { value, metadata } => PipelineData::Value(value, metadata), + PipelineDataHeader::ListStream { info, metadata } => { let handle = self.stream_manager().get_handle(); let reader = handle.read_stream(info.id, self.get_interface())?; - ListStream::new(reader, info.span, signals.clone()).into() + let ls = ListStream::new(reader, info.span, signals.clone()); + PipelineData::ListStream(ls, metadata) } - PipelineDataHeader::ByteStream(info) => { + PipelineDataHeader::ByteStream { info, metadata } => { let handle = self.stream_manager().get_handle(); let reader = handle.read_stream(info.id, self.get_interface())?; - ByteStream::from_result_iter(reader, info.span, signals.clone(), info.type_).into() + let bs = + ByteStream::from_result_iter(reader, info.span, signals.clone(), info.type_); + PipelineData::ByteStream(bs, metadata) } }) } @@ -245,26 +248,33 @@ pub trait Interface: Clone + Send { Ok::<_, ShellError>((id, writer)) }; match self.prepare_pipeline_data(data, context)? { - PipelineData::Value(value, ..) => { - Ok((PipelineDataHeader::Value(value), PipelineDataWriter::None)) - } + PipelineData::Value(value, metadata) => Ok(( + PipelineDataHeader::Value { value, metadata }, + PipelineDataWriter::None, + )), PipelineData::Empty => Ok((PipelineDataHeader::Empty, PipelineDataWriter::None)), - PipelineData::ListStream(stream, ..) => { + PipelineData::ListStream(stream, metadata) => { let (id, writer) = new_stream(LIST_STREAM_HIGH_PRESSURE)?; Ok(( - PipelineDataHeader::ListStream(ListStreamInfo { - id, - span: stream.span(), - }), + PipelineDataHeader::ListStream { + info: ListStreamInfo { + id, + span: stream.span(), + }, + metadata, + }, PipelineDataWriter::ListStream(writer, stream), )) } - PipelineData::ByteStream(stream, ..) => { + PipelineData::ByteStream(stream, metadata) => { let span = stream.span(); let type_ = stream.type_(); if let Some(reader) = stream.reader() { let (id, writer) = new_stream(RAW_STREAM_HIGH_PRESSURE)?; - let header = PipelineDataHeader::ByteStream(ByteStreamInfo { id, span, type_ }); + let header = PipelineDataHeader::ByteStream { + info: ByteStreamInfo { id, span, type_ }, + metadata, + }; Ok((header, PipelineDataWriter::ByteStream(writer, reader))) } else { Ok((PipelineDataHeader::Empty, PipelineDataWriter::None)) diff --git a/crates/nu-plugin-core/src/interface/tests.rs b/crates/nu-plugin-core/src/interface/tests.rs index 456eb547b5..03624ab90b 100644 --- a/crates/nu-plugin-core/src/interface/tests.rs +++ b/crates/nu-plugin-core/src/interface/tests.rs @@ -139,10 +139,20 @@ fn read_pipeline_data_empty() -> Result<(), ShellError> { fn read_pipeline_data_value() -> Result<(), ShellError> { let manager = TestInterfaceManager::new(&TestCase::new()); let value = Value::test_int(4); - let header = PipelineDataHeader::Value(value.clone()); + let metadata = Some(PipelineMetadata { + data_source: DataSource::None, + content_type: Some("foo".into()), + }); + let header = PipelineDataHeader::Value { + value: value.clone(), + metadata, + }; match manager.read_pipeline_data(header, &Signals::empty())? { - PipelineData::Value(read_value, ..) => assert_eq!(value, read_value), + PipelineData::Value(read_value, read_metadata) => { + assert_eq!(value, read_value); + assert_eq!(metadata, read_metadata); + } PipelineData::ListStream(..) => panic!("unexpected ListStream"), PipelineData::ByteStream(..) => panic!("unexpected ByteStream"), PipelineData::Empty => panic!("unexpected Empty"), @@ -163,11 +173,19 @@ fn read_pipeline_data_list_stream() -> Result<(), ShellError> { } test.add(StreamMessage::End(7)); - let header = PipelineDataHeader::ListStream(ListStreamInfo { - id: 7, - span: Span::test_data(), + let metadata = Some(PipelineMetadata { + data_source: DataSource::None, + content_type: Some("foobar".into()), }); + let header = PipelineDataHeader::ListStream { + info: ListStreamInfo { + id: 7, + span: Span::test_data(), + }, + metadata, + }; + let pipe = manager.read_pipeline_data(header, &Signals::empty())?; assert!( matches!(pipe, PipelineData::ListStream(..)), @@ -206,12 +224,21 @@ fn read_pipeline_data_byte_stream() -> Result<(), ShellError> { test.add(StreamMessage::End(12)); let test_span = Span::new(10, 13); - let header = PipelineDataHeader::ByteStream(ByteStreamInfo { - id: 12, - span: test_span, - type_: ByteStreamType::Unknown, + + let metadata = Some(PipelineMetadata { + data_source: DataSource::None, + content_type: Some("foobar".into()), }); + let header = PipelineDataHeader::ByteStream { + info: ByteStreamInfo { + id: 12, + span: test_span, + type_: ByteStreamType::Unknown, + }, + metadata, + }; + let pipe = manager.read_pipeline_data(header, &Signals::empty())?; // need to consume input @@ -253,10 +280,18 @@ fn read_pipeline_data_byte_stream() -> Result<(), ShellError> { #[test] fn read_pipeline_data_prepared_properly() -> Result<(), ShellError> { let manager = TestInterfaceManager::new(&TestCase::new()); - let header = PipelineDataHeader::ListStream(ListStreamInfo { - id: 0, - span: Span::test_data(), + let metadata = Some(PipelineMetadata { + data_source: DataSource::None, + content_type: Some("foobar".into()), }); + + let header = PipelineDataHeader::ListStream { + info: ListStreamInfo { + id: 0, + span: Span::test_data(), + }, + metadata, + }; match manager.read_pipeline_data(header, &Signals::empty())? { PipelineData::ListStream(_, meta) => match meta { Some(PipelineMetadata { data_source, .. }) => match data_source { @@ -303,7 +338,10 @@ fn write_pipeline_data_value() -> Result<(), ShellError> { interface.init_write_pipeline_data(PipelineData::Value(value.clone(), None), &())?; match header { - PipelineDataHeader::Value(read_value) => assert_eq!(value, read_value), + PipelineDataHeader::Value { + value: read_value, + metadata: None, + } => assert_eq!(value, read_value), _ => panic!("unexpected header: {header:?}"), } @@ -364,7 +402,7 @@ fn write_pipeline_data_list_stream() -> Result<(), ShellError> { let (header, writer) = interface.init_write_pipeline_data(pipe, &())?; let info = match header { - PipelineDataHeader::ListStream(info) => info, + PipelineDataHeader::ListStream { info, .. } => info, _ => panic!("unexpected header: {header:?}"), }; @@ -419,7 +457,10 @@ fn write_pipeline_data_byte_stream() -> Result<(), ShellError> { let (header, writer) = interface.init_write_pipeline_data(data, &())?; let info = match header { - PipelineDataHeader::ByteStream(info) => info, + PipelineDataHeader::ByteStream { + info, + metadata: None, + } => info, _ => panic!("unexpected header: {header:?}"), }; diff --git a/crates/nu-plugin-core/src/serializers/tests.rs b/crates/nu-plugin-core/src/serializers/tests.rs index 06d47ffc14..9be1902ebd 100644 --- a/crates/nu-plugin-core/src/serializers/tests.rs +++ b/crates/nu-plugin-core/src/serializers/tests.rs @@ -1,9 +1,9 @@ macro_rules! generate_tests { ($encoder:expr) => { use nu_plugin_protocol::{ - CallInfo, CustomValueOp, EvaluatedCall, PipelineDataHeader, PluginCall, - PluginCallResponse, PluginCustomValue, PluginInput, PluginOption, PluginOutput, - StreamData, + CallInfo, CustomValueOp, DataSource, EvaluatedCall, PipelineDataHeader, + PipelineMetadata, PluginCall, PluginCallResponse, PluginCustomValue, PluginInput, + PluginOption, PluginOutput, StreamData, }; use nu_protocol::{ LabeledError, PluginSignature, Signature, Span, Spanned, SyntaxShape, Value, @@ -123,10 +123,18 @@ macro_rules! generate_tests { )], }; + let metadata = Some(PipelineMetadata { + data_source: DataSource::None, + content_type: Some("foobar".into()), + }); + let plugin_call = PluginCall::Run(CallInfo { name: name.clone(), call: call.clone(), - input: PipelineDataHeader::Value(input.clone()), + input: PipelineDataHeader::Value { + value: input.clone(), + metadata, + }, }); let plugin_input = PluginInput::Call(1, plugin_call); @@ -144,7 +152,13 @@ macro_rules! generate_tests { match returned { PluginInput::Call(1, PluginCall::Run(call_info)) => { assert_eq!(name, call_info.name); - assert_eq!(PipelineDataHeader::Value(input), call_info.input); + assert_eq!( + PipelineDataHeader::Value { + value: input, + metadata + }, + call_info.input + ); assert_eq!(call.head, call_info.call.head); assert_eq!(call.positional.len(), call_info.call.positional.len()); diff --git a/crates/nu-plugin-protocol/src/lib.rs b/crates/nu-plugin-protocol/src/lib.rs index a9196a2d8d..5c379b1d57 100644 --- a/crates/nu-plugin-protocol/src/lib.rs +++ b/crates/nu-plugin-protocol/src/lib.rs @@ -23,7 +23,7 @@ pub mod test_util; use nu_protocol::{ ast::Operator, engine::Closure, ByteStreamType, Config, DeclId, LabeledError, PipelineData, - PluginMetadata, PluginSignature, ShellError, Span, Spanned, Value, + PipelineMetadata, PluginMetadata, PluginSignature, ShellError, Span, Spanned, Value, }; use nu_utils::SharedCow; use serde::{Deserialize, Serialize}; @@ -78,15 +78,24 @@ pub enum PipelineDataHeader { /// No input Empty, /// A single value - Value(Value), + Value { + value: Value, + metadata: Option, + }, /// Initiate [`nu_protocol::PipelineData::ListStream`]. /// /// Items are sent via [`StreamData`] - ListStream(ListStreamInfo), + ListStream { + info: ListStreamInfo, + metadata: Option, + }, /// Initiate [`nu_protocol::PipelineData::ByteStream`]. /// /// Items are sent via [`StreamData`] - ByteStream(ByteStreamInfo), + ByteStream { + info: ByteStreamInfo, + metadata: Option, + }, } impl PipelineDataHeader { @@ -94,9 +103,9 @@ impl PipelineDataHeader { pub fn stream_id(&self) -> Option { match self { PipelineDataHeader::Empty => None, - PipelineDataHeader::Value(_) => None, - PipelineDataHeader::ListStream(info) => Some(info.id), - PipelineDataHeader::ByteStream(info) => Some(info.id), + PipelineDataHeader::Value { .. } => None, + PipelineDataHeader::ListStream { info, .. } => Some(info.id), + PipelineDataHeader::ByteStream { info, .. } => Some(info.id), } } } @@ -342,7 +351,10 @@ impl PluginCallResponse { if value.is_nothing() { PluginCallResponse::PipelineData(PipelineDataHeader::Empty) } else { - PluginCallResponse::PipelineData(PipelineDataHeader::Value(value)) + PluginCallResponse::PipelineData(PipelineDataHeader::Value { + value, + metadata: None, + }) } } } diff --git a/crates/nu-protocol/src/pipeline/metadata.rs b/crates/nu-protocol/src/pipeline/metadata.rs index 1ed9222f46..fd3518bd64 100644 --- a/crates/nu-protocol/src/pipeline/metadata.rs +++ b/crates/nu-protocol/src/pipeline/metadata.rs @@ -1,7 +1,9 @@ use std::path::PathBuf; +use serde::{Deserialize, Serialize}; + /// Metadata that is valid for the whole [`PipelineData`](crate::PipelineData) -#[derive(Debug, Default, Clone)] +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] pub struct PipelineMetadata { pub data_source: DataSource, pub content_type: Option, @@ -27,7 +29,7 @@ impl PipelineMetadata { /// /// This can either be a particular family of commands (useful so downstream commands can adjust /// the presentation e.g. `Ls`) or the opened file to protect against overwrite-attempts properly. -#[derive(Debug, Default, Clone)] +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] pub enum DataSource { Ls, HtmlThemes, diff --git a/crates/nu_plugin_nu_example/nu_plugin_nu_example.nu b/crates/nu_plugin_nu_example/nu_plugin_nu_example.nu index e7c4f77365..524087699a 100755 --- a/crates/nu_plugin_nu_example/nu_plugin_nu_example.nu +++ b/crates/nu_plugin_nu_example/nu_plugin_nu_example.nu @@ -133,7 +133,7 @@ def process_call [ # Create a Value of type List that will be encoded and sent to Nushell let value = { - Value: { + Value: { value: { List: { vals: (0..9 | each { |x| { @@ -157,6 +157,7 @@ def process_call [ }), span: $span } + } } } @@ -265,4 +266,4 @@ def start_plugin [] { }) | each { from json | handle_input } | ignore -} \ No newline at end of file +} diff --git a/crates/nu_plugin_stress_internals/src/main.rs b/crates/nu_plugin_stress_internals/src/main.rs index 96023b44b7..d9a3940815 100644 --- a/crates/nu_plugin_stress_internals/src/main.rs +++ b/crates/nu_plugin_stress_internals/src/main.rs @@ -178,7 +178,7 @@ fn handle_message( id, { "PipelineData": { - "Value": return_value + "Value": {"value": return_value} } } ]