mod evaluated_call;
mod plugin_custom_value;
mod protocol_info;
#[cfg(test)]
mod tests;
#[cfg(test)]
pub(crate) mod test_util;
pub use evaluated_call::EvaluatedCall;
use nu_protocol::{PluginSignature, RawStream, ShellError, Span, Spanned, Value};
pub use plugin_custom_value::PluginCustomValue;
pub(crate) use protocol_info::ProtocolInfo;
use serde::{Deserialize, Serialize};
#[cfg(test)]
pub(crate) use protocol_info::Protocol;
pub type StreamId = usize;
pub type PluginCallId = usize;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CallInfo<D> {
pub name: String,
pub call: EvaluatedCall,
pub input: D,
pub config: Option<Value>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub enum PipelineDataHeader {
Empty,
Value(Value),
ListStream(ListStreamInfo),
ExternalStream(ExternalStreamInfo),
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct ListStreamInfo {
pub id: StreamId,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct ExternalStreamInfo {
pub span: Span,
pub stdout: Option<RawStreamInfo>,
pub stderr: Option<RawStreamInfo>,
pub exit_code: Option<ListStreamInfo>,
pub trim_end_newline: bool,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct RawStreamInfo {
pub id: StreamId,
pub is_binary: bool,
pub known_size: Option<u64>,
}
impl RawStreamInfo {
pub(crate) fn new(id: StreamId, stream: &RawStream) -> Self {
RawStreamInfo {
id,
is_binary: stream.is_binary,
known_size: stream.known_size,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum PluginCall<D> {
Signature,
Run(CallInfo<D>),
CustomValueOp(Spanned<PluginCustomValue>, CustomValueOp),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum CustomValueOp {
ToBaseValue,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum PluginInput {
Hello(ProtocolInfo),
Call(PluginCallId, PluginCall<PipelineDataHeader>),
Goodbye,
#[serde(untagged)]
Stream(StreamMessage),
}
impl TryFrom<PluginInput> for StreamMessage {
type Error = PluginInput;
fn try_from(msg: PluginInput) -> Result<StreamMessage, PluginInput> {
match msg {
PluginInput::Stream(stream_msg) => Ok(stream_msg),
_ => Err(msg),
}
}
}
impl From<StreamMessage> for PluginInput {
fn from(stream_msg: StreamMessage) -> PluginInput {
PluginInput::Stream(stream_msg)
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum StreamData {
List(Value),
Raw(Result<Vec<u8>, ShellError>),
}
impl From<Value> for StreamData {
fn from(value: Value) -> Self {
StreamData::List(value)
}
}
impl From<Result<Vec<u8>, ShellError>> for StreamData {
fn from(value: Result<Vec<u8>, ShellError>) -> Self {
StreamData::Raw(value)
}
}
impl TryFrom<StreamData> for Value {
type Error = ShellError;
fn try_from(data: StreamData) -> Result<Value, ShellError> {
match data {
StreamData::List(value) => Ok(value),
StreamData::Raw(_) => Err(ShellError::PluginFailedToDecode {
msg: "expected list stream data, found raw data".into(),
}),
}
}
}
impl TryFrom<StreamData> for Result<Vec<u8>, ShellError> {
type Error = ShellError;
fn try_from(data: StreamData) -> Result<Result<Vec<u8>, ShellError>, ShellError> {
match data {
StreamData::Raw(value) => Ok(value),
StreamData::List(_) => Err(ShellError::PluginFailedToDecode {
msg: "expected raw stream data, found list data".into(),
}),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum StreamMessage {
Data(StreamId, StreamData),
End(StreamId),
Drop(StreamId),
Ack(StreamId),
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub struct LabeledError {
pub label: String,
pub msg: String,
pub span: Option<Span>,
}
impl From<LabeledError> for ShellError {
fn from(error: LabeledError) -> Self {
if error.span.is_some() {
ShellError::GenericError {
error: error.label,
msg: error.msg,
span: error.span,
help: None,
inner: vec![],
}
} else {
ShellError::GenericError {
error: error.label,
msg: "".into(),
span: None,
help: (!error.msg.is_empty()).then_some(error.msg),
inner: vec![],
}
}
}
}
impl From<ShellError> for LabeledError {
fn from(error: ShellError) -> Self {
use miette::Diagnostic;
if let Some(labeled_span) = error.labels().and_then(|mut iter| iter.nth(0)) {
let offset = labeled_span.offset();
let span = Span::new(offset, offset + labeled_span.len());
LabeledError {
label: error.to_string(),
msg: labeled_span
.label()
.map(|label| label.to_owned())
.unwrap_or_else(|| "".into()),
span: Some(span),
}
} else {
LabeledError {
label: error.to_string(),
msg: error
.help()
.map(|help| help.to_string())
.unwrap_or_else(|| "".into()),
span: None,
}
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[doc(hidden)]
pub enum PluginCallResponse<D> {
Error(LabeledError),
Signature(Vec<PluginSignature>),
PipelineData(D),
}
impl PluginCallResponse<PipelineDataHeader> {
pub fn value(value: Value) -> PluginCallResponse<PipelineDataHeader> {
if value.is_nothing() {
PluginCallResponse::PipelineData(PipelineDataHeader::Empty)
} else {
PluginCallResponse::PipelineData(PipelineDataHeader::Value(value))
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[doc(hidden)]
pub enum PluginOutput {
Hello(ProtocolInfo),
CallResponse(PluginCallId, PluginCallResponse<PipelineDataHeader>),
#[serde(untagged)]
Stream(StreamMessage),
}
impl TryFrom<PluginOutput> for StreamMessage {
type Error = PluginOutput;
fn try_from(msg: PluginOutput) -> Result<StreamMessage, PluginOutput> {
match msg {
PluginOutput::Stream(stream_msg) => Ok(stream_msg),
_ => Err(msg),
}
}
}
impl From<StreamMessage> for PluginOutput {
fn from(stream_msg: StreamMessage) -> PluginOutput {
PluginOutput::Stream(stream_msg)
}
}