Make pipeline metadata available to plugins (#13495)

# Description
Fixes an issue with pipeline metadata not being passed to plugins.
This commit is contained in:
Jack Wright 2024-08-02 11:01:20 -07:00 committed by GitHub
parent ca8eb856e8
commit d081e3386f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 208 additions and 130 deletions

View File

@ -177,16 +177,19 @@ pub trait InterfaceManager {
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
self.prepare_pipeline_data(match header { self.prepare_pipeline_data(match header {
PipelineDataHeader::Empty => PipelineData::Empty, PipelineDataHeader::Empty => PipelineData::Empty,
PipelineDataHeader::Value(value) => PipelineData::Value(value, None), PipelineDataHeader::Value(value, metadata) => PipelineData::Value(value, metadata),
PipelineDataHeader::ListStream(info) => { PipelineDataHeader::ListStream(info) => {
let handle = self.stream_manager().get_handle(); let handle = self.stream_manager().get_handle();
let reader = handle.read_stream(info.id, self.get_interface())?; 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, info.metadata)
} }
PipelineDataHeader::ByteStream(info) => { PipelineDataHeader::ByteStream(info) => {
let handle = self.stream_manager().get_handle(); let handle = self.stream_manager().get_handle();
let reader = handle.read_stream(info.id, self.get_interface())?; 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, info.metadata)
} }
}) })
} }
@ -248,26 +251,33 @@ pub trait Interface: Clone + Send {
Ok::<_, ShellError>((id, writer)) Ok::<_, ShellError>((id, writer))
}; };
match self.prepare_pipeline_data(data, context)? { match self.prepare_pipeline_data(data, context)? {
PipelineData::Value(value, ..) => { PipelineData::Value(value, metadata) => Ok((
Ok((PipelineDataHeader::Value(value), PipelineDataWriter::None)) PipelineDataHeader::Value(value, metadata),
} PipelineDataWriter::None,
)),
PipelineData::Empty => Ok((PipelineDataHeader::Empty, 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)?; let (id, writer) = new_stream(LIST_STREAM_HIGH_PRESSURE)?;
Ok(( Ok((
PipelineDataHeader::ListStream(ListStreamInfo { PipelineDataHeader::ListStream(ListStreamInfo {
id, id,
span: stream.span(), span: stream.span(),
metadata,
}), }),
PipelineDataWriter::ListStream(writer, stream), PipelineDataWriter::ListStream(writer, stream),
)) ))
} }
PipelineData::ByteStream(stream, ..) => { PipelineData::ByteStream(stream, metadata) => {
let span = stream.span(); let span = stream.span();
let type_ = stream.type_(); let type_ = stream.type_();
if let Some(reader) = stream.reader() { if let Some(reader) = stream.reader() {
let (id, writer) = new_stream(RAW_STREAM_HIGH_PRESSURE)?; let (id, writer) = new_stream(RAW_STREAM_HIGH_PRESSURE)?;
let header = PipelineDataHeader::ByteStream(ByteStreamInfo { id, span, type_ }); let header = PipelineDataHeader::ByteStream(ByteStreamInfo {
id,
span,
type_,
metadata,
});
Ok((header, PipelineDataWriter::ByteStream(writer, reader))) Ok((header, PipelineDataWriter::ByteStream(writer, reader)))
} else { } else {
Ok((PipelineDataHeader::Empty, PipelineDataWriter::None)) Ok((PipelineDataHeader::Empty, PipelineDataWriter::None))

View File

@ -137,10 +137,16 @@ fn read_pipeline_data_empty() -> Result<(), ShellError> {
fn read_pipeline_data_value() -> Result<(), ShellError> { fn read_pipeline_data_value() -> Result<(), ShellError> {
let manager = TestInterfaceManager::new(&TestCase::new()); let manager = TestInterfaceManager::new(&TestCase::new());
let value = Value::test_int(4); let value = Value::test_int(4);
let header = PipelineDataHeader::Value(value.clone()); let metadata = Some(PipelineMetadata {
data_source: DataSource::FilePath("/test/path".into()),
content_type: None,
});
let header = PipelineDataHeader::Value(value.clone(), metadata.clone());
match manager.read_pipeline_data(header, &Signals::empty())? { 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::ListStream(..) => panic!("unexpected ListStream"),
PipelineData::ByteStream(..) => panic!("unexpected ByteStream"), PipelineData::ByteStream(..) => panic!("unexpected ByteStream"),
PipelineData::Empty => panic!("unexpected Empty"), PipelineData::Empty => panic!("unexpected Empty"),
@ -161,9 +167,15 @@ fn read_pipeline_data_list_stream() -> Result<(), ShellError> {
} }
test.add(StreamMessage::End(7)); test.add(StreamMessage::End(7));
let metadata = Some(PipelineMetadata {
data_source: DataSource::None,
content_type: Some("foobar".into()),
});
let header = PipelineDataHeader::ListStream(ListStreamInfo { let header = PipelineDataHeader::ListStream(ListStreamInfo {
id: 7, id: 7,
span: Span::test_data(), span: Span::test_data(),
metadata,
}); });
let pipe = manager.read_pipeline_data(header, &Signals::empty())?; let pipe = manager.read_pipeline_data(header, &Signals::empty())?;
@ -204,10 +216,17 @@ fn read_pipeline_data_byte_stream() -> Result<(), ShellError> {
test.add(StreamMessage::End(12)); test.add(StreamMessage::End(12));
let test_span = Span::new(10, 13); let test_span = Span::new(10, 13);
let metadata = Some(PipelineMetadata {
data_source: DataSource::None,
content_type: Some("foobar".into()),
});
let header = PipelineDataHeader::ByteStream(ByteStreamInfo { let header = PipelineDataHeader::ByteStream(ByteStreamInfo {
id: 12, id: 12,
span: test_span, span: test_span,
type_: ByteStreamType::Unknown, type_: ByteStreamType::Unknown,
metadata,
}); });
let pipe = manager.read_pipeline_data(header, &Signals::empty())?; let pipe = manager.read_pipeline_data(header, &Signals::empty())?;
@ -251,9 +270,15 @@ fn read_pipeline_data_byte_stream() -> Result<(), ShellError> {
#[test] #[test]
fn read_pipeline_data_prepared_properly() -> Result<(), ShellError> { fn read_pipeline_data_prepared_properly() -> Result<(), ShellError> {
let manager = TestInterfaceManager::new(&TestCase::new()); let manager = TestInterfaceManager::new(&TestCase::new());
let metadata = Some(PipelineMetadata {
data_source: DataSource::None,
content_type: Some("foobar".into()),
});
let header = PipelineDataHeader::ListStream(ListStreamInfo { let header = PipelineDataHeader::ListStream(ListStreamInfo {
id: 0, id: 0,
span: Span::test_data(), span: Span::test_data(),
metadata,
}); });
match manager.read_pipeline_data(header, &Signals::empty())? { match manager.read_pipeline_data(header, &Signals::empty())? {
PipelineData::ListStream(_, meta) => match meta { PipelineData::ListStream(_, meta) => match meta {
@ -301,7 +326,7 @@ fn write_pipeline_data_value() -> Result<(), ShellError> {
interface.init_write_pipeline_data(PipelineData::Value(value.clone(), None), &())?; interface.init_write_pipeline_data(PipelineData::Value(value.clone(), None), &())?;
match header { match header {
PipelineDataHeader::Value(read_value) => assert_eq!(value, read_value), PipelineDataHeader::Value(read_value, _) => assert_eq!(value, read_value),
_ => panic!("unexpected header: {header:?}"), _ => panic!("unexpected header: {header:?}"),
} }

View File

@ -6,7 +6,8 @@ macro_rules! generate_tests {
StreamData, StreamData,
}; };
use nu_protocol::{ use nu_protocol::{
LabeledError, PluginSignature, Signature, Span, Spanned, SyntaxShape, Value, DataSource, LabeledError, PipelineMetadata, PluginSignature, Signature, Span, Spanned,
SyntaxShape, Value,
}; };
#[test] #[test]
@ -123,10 +124,15 @@ macro_rules! generate_tests {
)], )],
}; };
let metadata = Some(PipelineMetadata {
data_source: DataSource::None,
content_type: Some("foobar".into()),
});
let plugin_call = PluginCall::Run(CallInfo { let plugin_call = PluginCall::Run(CallInfo {
name: name.clone(), name: name.clone(),
call: call.clone(), call: call.clone(),
input: PipelineDataHeader::Value(input.clone()), input: PipelineDataHeader::Value(input.clone(), metadata.clone()),
}); });
let plugin_input = PluginInput::Call(1, plugin_call); let plugin_input = PluginInput::Call(1, plugin_call);
@ -144,7 +150,7 @@ macro_rules! generate_tests {
match returned { match returned {
PluginInput::Call(1, PluginCall::Run(call_info)) => { PluginInput::Call(1, PluginCall::Run(call_info)) => {
assert_eq!(name, call_info.name); assert_eq!(name, call_info.name);
assert_eq!(PipelineDataHeader::Value(input), call_info.input); assert_eq!(PipelineDataHeader::Value(input, metadata), call_info.input);
assert_eq!(call.head, call_info.call.head); assert_eq!(call.head, call_info.call.head);
assert_eq!(call.positional.len(), call_info.call.positional.len()); assert_eq!(call.positional.len(), call_info.call.positional.len());
@ -305,7 +311,7 @@ macro_rules! generate_tests {
match returned { match returned {
PluginOutput::CallResponse( PluginOutput::CallResponse(
4, 4,
PluginCallResponse::PipelineData(PipelineDataHeader::Value(returned_value)), PluginCallResponse::PipelineData(PipelineDataHeader::Value(returned_value, _)),
) => { ) => {
assert_eq!(value, returned_value) assert_eq!(value, returned_value)
} }
@ -325,7 +331,7 @@ macro_rules! generate_tests {
span, span,
); );
let response = PluginCallResponse::PipelineData(PipelineDataHeader::Value(value)); let response = PluginCallResponse::PipelineData(PipelineDataHeader::value(value));
let output = PluginOutput::CallResponse(5, response); let output = PluginOutput::CallResponse(5, response);
let encoder = $encoder; let encoder = $encoder;
@ -341,7 +347,7 @@ macro_rules! generate_tests {
match returned { match returned {
PluginOutput::CallResponse( PluginOutput::CallResponse(
5, 5,
PluginCallResponse::PipelineData(PipelineDataHeader::Value(returned_value)), PluginCallResponse::PipelineData(PipelineDataHeader::Value(returned_value, _)),
) => { ) => {
assert_eq!(span, returned_value.span()); assert_eq!(span, returned_value.span());

View File

@ -17,8 +17,9 @@ use nu_plugin_protocol::{
use nu_protocol::{ use nu_protocol::{
ast::{Math, Operator}, ast::{Math, Operator},
engine::Closure, engine::Closure,
ByteStreamType, CustomValue, IntoInterruptiblePipelineData, IntoSpanned, PipelineData, ByteStreamType, CustomValue, DataSource, IntoInterruptiblePipelineData, IntoSpanned,
PluginMetadata, PluginSignature, ShellError, Signals, Span, Spanned, Value, PipelineData, PipelineMetadata, PluginMetadata, PluginSignature, ShellError, Signals, Span,
Spanned, Value,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
@ -52,10 +53,7 @@ fn manager_consume_all_exits_after_streams_and_interfaces_are_dropped() -> Resul
// Create a stream... // Create a stream...
let stream = manager.read_pipeline_data( let stream = manager.read_pipeline_data(
PipelineDataHeader::ListStream(ListStreamInfo { PipelineDataHeader::list_stream(ListStreamInfo::new(0, Span::test_data())),
id: 0,
span: Span::test_data(),
}),
&Signals::empty(), &Signals::empty(),
)?; )?;
@ -108,10 +106,7 @@ fn manager_consume_all_propagates_io_error_to_readers() -> Result<(), ShellError
test.set_read_error(test_io_error()); test.set_read_error(test_io_error());
let stream = manager.read_pipeline_data( let stream = manager.read_pipeline_data(
PipelineDataHeader::ListStream(ListStreamInfo { PipelineDataHeader::list_stream(ListStreamInfo::new(0, Span::test_data())),
id: 0,
span: Span::test_data(),
}),
&Signals::empty(), &Signals::empty(),
)?; )?;
@ -154,11 +149,11 @@ fn manager_consume_all_propagates_message_error_to_readers() -> Result<(), Shell
test.add(invalid_output()); test.add(invalid_output());
let stream = manager.read_pipeline_data( let stream = manager.read_pipeline_data(
PipelineDataHeader::ByteStream(ByteStreamInfo { PipelineDataHeader::byte_stream(ByteStreamInfo::new(
id: 0, 0,
span: Span::test_data(), Span::test_data(),
type_: ByteStreamType::Unknown, ByteStreamType::Unknown,
}), )),
&Signals::empty(), &Signals::empty(),
)?; )?;
@ -331,10 +326,10 @@ fn manager_consume_call_response_forwards_to_subscriber_with_pipeline_data(
manager.consume(PluginOutput::CallResponse( manager.consume(PluginOutput::CallResponse(
0, 0,
PluginCallResponse::PipelineData(PipelineDataHeader::ListStream(ListStreamInfo { PluginCallResponse::PipelineData(PipelineDataHeader::list_stream(ListStreamInfo::new(
id: 0, 0,
span: Span::test_data(), Span::test_data(),
})), ))),
))?; ))?;
for i in 0..2 { for i in 0..2 {
@ -375,18 +370,18 @@ fn manager_consume_call_response_registers_streams() -> Result<(), ShellError> {
// Check list streams, byte streams // Check list streams, byte streams
manager.consume(PluginOutput::CallResponse( manager.consume(PluginOutput::CallResponse(
0, 0,
PluginCallResponse::PipelineData(PipelineDataHeader::ListStream(ListStreamInfo { PluginCallResponse::PipelineData(PipelineDataHeader::list_stream(ListStreamInfo::new(
id: 0, 0,
span: Span::test_data(), Span::test_data(),
})), ))),
))?; ))?;
manager.consume(PluginOutput::CallResponse( manager.consume(PluginOutput::CallResponse(
1, 1,
PluginCallResponse::PipelineData(PipelineDataHeader::ByteStream(ByteStreamInfo { PluginCallResponse::PipelineData(PipelineDataHeader::byte_stream(ByteStreamInfo::new(
id: 1, 1,
span: Span::test_data(), Span::test_data(),
type_: ByteStreamType::Unknown, ByteStreamType::Unknown,
})), ))),
))?; ))?;
// ListStream should have one // ListStream should have one
@ -442,10 +437,7 @@ fn manager_consume_engine_call_forwards_to_subscriber_with_pipeline_data() -> Re
span: Span::test_data(), span: Span::test_data(),
}, },
positional: vec![], positional: vec![],
input: PipelineDataHeader::ListStream(ListStreamInfo { input: PipelineDataHeader::list_stream(ListStreamInfo::new(2, Span::test_data())),
id: 2,
span: Span::test_data(),
}),
redirect_stdout: false, redirect_stdout: false,
redirect_stderr: false, redirect_stderr: false,
}, },
@ -806,6 +798,11 @@ fn interface_write_plugin_call_writes_run_with_value_input() -> Result<(), Shell
let manager = test.plugin("test"); let manager = test.plugin("test");
let interface = manager.get_interface(); let interface = manager.get_interface();
let metadata0 = PipelineMetadata {
data_source: DataSource::None,
content_type: Some("baz".into()),
};
let result = interface.write_plugin_call( let result = interface.write_plugin_call(
PluginCall::Run(CallInfo { PluginCall::Run(CallInfo {
name: "foo".into(), name: "foo".into(),
@ -814,7 +811,7 @@ fn interface_write_plugin_call_writes_run_with_value_input() -> Result<(), Shell
positional: vec![], positional: vec![],
named: vec![], named: vec![],
}, },
input: PipelineData::Value(Value::test_int(-1), None), input: PipelineData::Value(Value::test_int(-1), Some(metadata0.clone())),
}), }),
None, None,
)?; )?;
@ -826,7 +823,10 @@ fn interface_write_plugin_call_writes_run_with_value_input() -> Result<(), Shell
PluginCall::Run(CallInfo { name, input, .. }) => { PluginCall::Run(CallInfo { name, input, .. }) => {
assert_eq!("foo", name); assert_eq!("foo", name);
match input { match input {
PipelineDataHeader::Value(value) => assert_eq!(-1, value.as_int()?), PipelineDataHeader::Value(value, metadata) => {
assert_eq!(-1, value.as_int()?);
assert_eq!(metadata0, metadata.expect("there should be metadata"));
}
_ => panic!("unexpected input header: {input:?}"), _ => panic!("unexpected input header: {input:?}"),
} }
} }

View File

@ -23,7 +23,7 @@ pub mod test_util;
use nu_protocol::{ use nu_protocol::{
ast::Operator, engine::Closure, ByteStreamType, Config, DeclId, LabeledError, PipelineData, 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 nu_utils::SharedCow;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -78,7 +78,7 @@ pub enum PipelineDataHeader {
/// No input /// No input
Empty, Empty,
/// A single value /// A single value
Value(Value), Value(Value, Option<PipelineMetadata>),
/// Initiate [`nu_protocol::PipelineData::ListStream`]. /// Initiate [`nu_protocol::PipelineData::ListStream`].
/// ///
/// Items are sent via [`StreamData`] /// Items are sent via [`StreamData`]
@ -94,11 +94,23 @@ impl PipelineDataHeader {
pub fn stream_id(&self) -> Option<StreamId> { pub fn stream_id(&self) -> Option<StreamId> {
match self { match self {
PipelineDataHeader::Empty => None, PipelineDataHeader::Empty => None,
PipelineDataHeader::Value(_) => None, PipelineDataHeader::Value(_, _) => None,
PipelineDataHeader::ListStream(info) => Some(info.id), PipelineDataHeader::ListStream(info) => Some(info.id),
PipelineDataHeader::ByteStream(info) => Some(info.id), PipelineDataHeader::ByteStream(info) => Some(info.id),
} }
} }
pub fn value(value: Value) -> Self {
PipelineDataHeader::Value(value, None)
}
pub fn list_stream(info: ListStreamInfo) -> Self {
PipelineDataHeader::ListStream(info)
}
pub fn byte_stream(info: ByteStreamInfo) -> Self {
PipelineDataHeader::ByteStream(info)
}
} }
/// Additional information about list (value) streams /// Additional information about list (value) streams
@ -106,6 +118,18 @@ impl PipelineDataHeader {
pub struct ListStreamInfo { pub struct ListStreamInfo {
pub id: StreamId, pub id: StreamId,
pub span: Span, pub span: Span,
pub metadata: Option<PipelineMetadata>,
}
impl ListStreamInfo {
/// Create a new `ListStreamInfo` with a unique ID
pub fn new(id: StreamId, span: Span) -> Self {
ListStreamInfo {
id,
span,
metadata: None,
}
}
} }
/// Additional information about byte streams /// Additional information about byte streams
@ -115,6 +139,19 @@ pub struct ByteStreamInfo {
pub span: Span, pub span: Span,
#[serde(rename = "type")] #[serde(rename = "type")]
pub type_: ByteStreamType, pub type_: ByteStreamType,
pub metadata: Option<PipelineMetadata>,
}
impl ByteStreamInfo {
/// Create a new `ByteStreamInfo` with a unique ID
pub fn new(id: StreamId, span: Span, type_: ByteStreamType) -> Self {
ByteStreamInfo {
id,
span,
type_,
metadata: None,
}
}
} }
/// Calls that a plugin can execute. The type parameter determines the input type. /// Calls that a plugin can execute. The type parameter determines the input type.
@ -344,7 +381,7 @@ impl PluginCallResponse<PipelineDataHeader> {
if value.is_nothing() { if value.is_nothing() {
PluginCallResponse::PipelineData(PipelineDataHeader::Empty) PluginCallResponse::PipelineData(PipelineDataHeader::Empty)
} else { } else {
PluginCallResponse::PipelineData(PipelineDataHeader::Value(value)) PluginCallResponse::PipelineData(PipelineDataHeader::value(value))
} }
} }
} }

View File

@ -55,10 +55,7 @@ fn manager_consume_all_exits_after_streams_and_interfaces_are_dropped() -> Resul
// Create a stream... // Create a stream...
let stream = manager.read_pipeline_data( let stream = manager.read_pipeline_data(
PipelineDataHeader::ListStream(ListStreamInfo { PipelineDataHeader::list_stream(ListStreamInfo::new(0, Span::test_data())),
id: 0,
span: Span::test_data(),
}),
&Signals::empty(), &Signals::empty(),
)?; )?;
@ -111,10 +108,7 @@ fn manager_consume_all_propagates_io_error_to_readers() -> Result<(), ShellError
test.set_read_error(test_io_error()); test.set_read_error(test_io_error());
let stream = manager.read_pipeline_data( let stream = manager.read_pipeline_data(
PipelineDataHeader::ListStream(ListStreamInfo { PipelineDataHeader::list_stream(ListStreamInfo::new(0, Span::test_data())),
id: 0,
span: Span::test_data(),
}),
&Signals::empty(), &Signals::empty(),
)?; )?;
@ -157,11 +151,11 @@ fn manager_consume_all_propagates_message_error_to_readers() -> Result<(), Shell
test.add(invalid_input()); test.add(invalid_input());
let stream = manager.read_pipeline_data( let stream = manager.read_pipeline_data(
PipelineDataHeader::ByteStream(ByteStreamInfo { PipelineDataHeader::byte_stream(ByteStreamInfo::new(
id: 0, 0,
span: Span::test_data(), Span::test_data(),
type_: ByteStreamType::Unknown, ByteStreamType::Unknown,
}), )),
&Signals::empty(), &Signals::empty(),
)?; )?;
@ -414,10 +408,7 @@ fn manager_consume_call_run_forwards_to_receiver_with_pipeline_data() -> Result<
positional: vec![], positional: vec![],
named: vec![], named: vec![],
}, },
input: PipelineDataHeader::ListStream(ListStreamInfo { input: PipelineDataHeader::list_stream(ListStreamInfo::new(6, Span::test_data())),
id: 6,
span: Span::test_data(),
}),
}), }),
))?; ))?;
@ -556,10 +547,10 @@ fn manager_consume_engine_call_response_forwards_to_subscriber_with_pipeline_dat
manager.consume(PluginInput::EngineCallResponse( manager.consume(PluginInput::EngineCallResponse(
0, 0,
EngineCallResponse::PipelineData(PipelineDataHeader::ListStream(ListStreamInfo { EngineCallResponse::PipelineData(PipelineDataHeader::list_stream(ListStreamInfo::new(
id: 0, 0,
span: Span::test_data(), Span::test_data(),
})), ))),
))?; ))?;
for i in 0..2 { for i in 0..2 {
@ -707,7 +698,7 @@ fn interface_write_response_with_value() -> Result<(), ShellError> {
assert_eq!(33, id, "id"); assert_eq!(33, id, "id");
match response { match response {
PluginCallResponse::PipelineData(header) => match header { PluginCallResponse::PipelineData(header) => match header {
PipelineDataHeader::Value(value) => assert_eq!(6, value.as_int()?), PipelineDataHeader::Value(value, _) => assert_eq!(6, value.as_int()?),
_ => panic!("unexpected pipeline data header: {header:?}"), _ => panic!("unexpected pipeline data header: {header:?}"),
}, },
_ => panic!("unexpected response: {response:?}"), _ => panic!("unexpected response: {response:?}"),

View File

@ -1,7 +1,9 @@
use std::path::PathBuf; use std::path::PathBuf;
use serde::{Deserialize, Serialize};
/// Metadata that is valid for the whole [`PipelineData`](crate::PipelineData) /// Metadata that is valid for the whole [`PipelineData`](crate::PipelineData)
#[derive(Debug, Default, Clone)] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct PipelineMetadata { pub struct PipelineMetadata {
pub data_source: DataSource, pub data_source: DataSource,
pub content_type: Option<String>, pub content_type: Option<String>,
@ -27,7 +29,7 @@ impl PipelineMetadata {
/// ///
/// This can either be a particular family of commands (useful so downstream commands can adjust /// 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. /// the presentation e.g. `Ls`) or the opened file to protect against overwrite-attempts properly.
#[derive(Debug, Default, Clone)] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub enum DataSource { pub enum DataSource {
Ls, Ls,
HtmlThemes, HtmlThemes,

View File

@ -7,7 +7,7 @@
# language without adding any extra dependencies to our tests. # language without adding any extra dependencies to our tests.
const NUSHELL_VERSION = "0.96.2" const NUSHELL_VERSION = "0.96.2"
const PLUGIN_VERSION = "0.1.0" # bump if you change commands! const PLUGIN_VERSION = "0.1.1" # bump if you change commands!
def main [--stdio] { def main [--stdio] {
if ($stdio) { if ($stdio) {
@ -133,7 +133,7 @@ def process_call [
# Create a Value of type List that will be encoded and sent to Nushell # Create a Value of type List that will be encoded and sent to Nushell
let value = { let value = {
Value: { Value: [{
List: { List: {
vals: (0..9 | each { |x| vals: (0..9 | each { |x|
{ {
@ -157,7 +157,7 @@ def process_call [
}), }),
span: $span span: $span
} }
} }, null]
} }
write_response $id { PipelineData: $value } write_response $id { PipelineData: $value }

View File

@ -28,7 +28,7 @@ import json
NUSHELL_VERSION = "0.96.2" NUSHELL_VERSION = "0.96.2"
PLUGIN_VERSION = "0.1.0" # bump if you change commands! PLUGIN_VERSION = "0.1.1" # bump if you change commands!
def signatures(): def signatures():
@ -125,15 +125,12 @@ def process_call(id, plugin_call):
span = plugin_call["call"]["head"] span = plugin_call["call"]["head"]
# Creates a Value of type List that will be encoded and sent to Nushell # Creates a Value of type List that will be encoded and sent to Nushell
def f(x, y): return { def f(x, y):
"Int": { return {"Int": {"val": x * y, "span": span}}
"val": x * y,
"span": span
}
}
value = { value = {
"Value": { "Value": [
{
"List": { "List": {
"vals": [ "vals": [
{ {
@ -143,13 +140,16 @@ def process_call(id, plugin_call):
"two": f(x, 1), "two": f(x, 1),
"three": f(x, 2), "three": f(x, 2),
}, },
"span": span "span": span,
} }
} for x in range(0, 10) }
for x in range(0, 10)
], ],
"span": span "span": span,
}
} }
},
None,
]
} }
write_response(id, {"PipelineData": value}) write_response(id, {"PipelineData": value})
@ -172,7 +172,7 @@ def tell_nushell_hello():
"Hello": { "Hello": {
"protocol": "nu-plugin", # always this value "protocol": "nu-plugin", # always this value
"version": NUSHELL_VERSION, "version": NUSHELL_VERSION,
"features": [] "features": [],
} }
} }
sys.stdout.write(json.dumps(hello)) sys.stdout.write(json.dumps(hello))
@ -200,7 +200,8 @@ def write_error(id, text, span=None):
Use this error format to send errors to nushell in response to a plugin call. The ID of the Use this error format to send errors to nushell in response to a plugin call. The ID of the
plugin call is required. plugin call is required.
""" """
error = { error = (
{
"Error": { "Error": {
"msg": "ERROR from plugin", "msg": "ERROR from plugin",
"labels": [ "labels": [
@ -210,12 +211,15 @@ def write_error(id, text, span=None):
} }
], ],
} }
} if span is not None else { }
if span is not None
else {
"Error": { "Error": {
"msg": "ERROR from plugin", "msg": "ERROR from plugin",
"help": text, "help": text,
} }
} }
)
write_response(id, error) write_response(id, error)
@ -230,11 +234,14 @@ def handle_input(input):
elif "Call" in input: elif "Call" in input:
[id, plugin_call] = input["Call"] [id, plugin_call] = input["Call"]
if plugin_call == "Metadata": if plugin_call == "Metadata":
write_response(id, { write_response(
id,
{
"Metadata": { "Metadata": {
"version": PLUGIN_VERSION, "version": PLUGIN_VERSION,
} }
}) },
)
elif plugin_call == "Signature": elif plugin_call == "Signature":
write_response(id, signatures()) write_response(id, signatures())
elif "Run" in plugin_call: elif "Run" in plugin_call:

View File

@ -178,7 +178,7 @@ fn handle_message(
id, id,
{ {
"PipelineData": { "PipelineData": {
"Value": return_value "Value": [return_value, null]
} }
} }
] ]