mod evaluated_call;
mod plugin_custom_value;
mod protocol_info;
#[cfg(test)]
mod tests;
#[doc(hidden)]
pub mod test_util;
use nu_protocol::{
BlockId, ByteStreamType, Config, DeclId, DynamicSuggestion, LabeledError, PipelineData,
PipelineMetadata, PluginMetadata, PluginSignature, ShellError, SignalAction, Span, Spanned,
Value, ast,
ast::Operator,
casing::Casing,
engine::{ArgType, Closure},
ir::IrBlock,
};
use nu_utils::SharedCow;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, path::PathBuf};
pub use evaluated_call::EvaluatedCall;
pub use plugin_custom_value::PluginCustomValue;
#[allow(unused_imports)] pub use protocol_info::{Feature, Protocol, ProtocolInfo};
pub type StreamId = usize;
pub type PluginCallId = usize;
pub type EngineCallId = usize;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CallInfo<D> {
pub name: String,
pub call: EvaluatedCall,
pub input: D,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum GetCompletionArgType {
Flag(String),
Positional(usize),
}
impl<'a> From<GetCompletionArgType> for ArgType<'a> {
fn from(value: GetCompletionArgType) -> Self {
match value {
GetCompletionArgType::Flag(flag_name) => {
ArgType::Flag(std::borrow::Cow::from(flag_name))
}
GetCompletionArgType::Positional(idx) => ArgType::Positional(idx),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct DynamicCompletionCall {
pub call: ast::Call,
pub strip: bool,
pub pos: usize,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GetCompletionInfo {
pub name: String,
pub arg_type: GetCompletionArgType,
pub call: DynamicCompletionCall,
}
impl<D> CallInfo<D> {
pub fn map_data<T>(
self,
f: impl FnOnce(D) -> Result<T, ShellError>,
) -> Result<CallInfo<T>, ShellError> {
Ok(CallInfo {
name: self.name,
call: self.call,
input: f(self.input)?,
})
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub enum PipelineDataHeader {
Empty,
Value(Value, Option<PipelineMetadata>),
ListStream(ListStreamInfo),
ByteStream(ByteStreamInfo),
}
impl PipelineDataHeader {
pub fn stream_id(&self) -> Option<StreamId> {
match self {
PipelineDataHeader::Empty => None,
PipelineDataHeader::Value(_, _) => None,
PipelineDataHeader::ListStream(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)
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct ListStreamInfo {
pub id: StreamId,
pub span: Span,
pub metadata: Option<PipelineMetadata>,
}
impl ListStreamInfo {
pub fn new(id: StreamId, span: Span) -> Self {
ListStreamInfo {
id,
span,
metadata: None,
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct ByteStreamInfo {
pub id: StreamId,
pub span: Span,
#[serde(rename = "type")]
pub type_: ByteStreamType,
pub metadata: Option<PipelineMetadata>,
}
impl ByteStreamInfo {
pub fn new(id: StreamId, span: Span, type_: ByteStreamType) -> Self {
ByteStreamInfo {
id,
span,
type_,
metadata: None,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum PluginCall<D> {
Metadata,
Signature,
Run(CallInfo<D>),
GetCompletion(GetCompletionInfo),
CustomValueOp(Spanned<PluginCustomValue>, CustomValueOp),
}
impl<D> PluginCall<D> {
pub fn map_data<T>(
self,
f: impl FnOnce(D) -> Result<T, ShellError>,
) -> Result<PluginCall<T>, ShellError> {
Ok(match self {
PluginCall::Metadata => PluginCall::Metadata,
PluginCall::Signature => PluginCall::Signature,
PluginCall::GetCompletion(flag_name) => PluginCall::GetCompletion(flag_name),
PluginCall::Run(call) => PluginCall::Run(call.map_data(f)?),
PluginCall::CustomValueOp(custom_value, op) => {
PluginCall::CustomValueOp(custom_value, op)
}
})
}
pub fn span(&self) -> Option<Span> {
match self {
PluginCall::Metadata => None,
PluginCall::Signature => None,
PluginCall::GetCompletion(_) => None,
PluginCall::Run(CallInfo { call, .. }) => Some(call.head),
PluginCall::CustomValueOp(val, _) => Some(val.span),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum CustomValueOp {
ToBaseValue,
FollowPathInt {
index: Spanned<usize>,
optional: bool,
},
FollowPathString {
column_name: Spanned<String>,
optional: bool,
casing: Casing,
},
PartialCmp(Value),
Operation(Spanned<Operator>, Value),
Save {
path: Spanned<PathBuf>,
save_call_span: Span,
},
Dropped,
}
impl CustomValueOp {
pub fn name(&self) -> &'static str {
match self {
CustomValueOp::ToBaseValue => "to_base_value",
CustomValueOp::FollowPathInt { .. } => "follow_path_int",
CustomValueOp::FollowPathString { .. } => "follow_path_string",
CustomValueOp::PartialCmp(_) => "partial_cmp",
CustomValueOp::Operation(_, _) => "operation",
CustomValueOp::Save { .. } => "save",
CustomValueOp::Dropped => "dropped",
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum PluginInput {
Hello(ProtocolInfo),
Call(PluginCallId, PluginCall<PipelineDataHeader>),
Goodbye,
EngineCallResponse(EngineCallId, EngineCallResponse<PipelineDataHeader>),
Data(StreamId, StreamData),
End(StreamId),
Drop(StreamId),
Ack(StreamId),
Signal(SignalAction),
}
impl TryFrom<PluginInput> for StreamMessage {
type Error = PluginInput;
fn try_from(msg: PluginInput) -> Result<StreamMessage, PluginInput> {
match msg {
PluginInput::Data(id, data) => Ok(StreamMessage::Data(id, data)),
PluginInput::End(id) => Ok(StreamMessage::End(id)),
PluginInput::Drop(id) => Ok(StreamMessage::Drop(id)),
PluginInput::Ack(id) => Ok(StreamMessage::Ack(id)),
_ => Err(msg),
}
}
}
impl From<StreamMessage> for PluginInput {
fn from(stream_msg: StreamMessage) -> PluginInput {
match stream_msg {
StreamMessage::Data(id, data) => PluginInput::Data(id, data),
StreamMessage::End(id) => PluginInput::End(id),
StreamMessage::Drop(id) => PluginInput::Drop(id),
StreamMessage::Ack(id) => PluginInput::Ack(id),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum StreamData {
List(Value),
Raw(Result<Vec<u8>, LabeledError>),
}
impl From<Value> for StreamData {
fn from(value: Value) -> Self {
StreamData::List(value)
}
}
impl From<Result<Vec<u8>, LabeledError>> for StreamData {
fn from(value: Result<Vec<u8>, LabeledError>) -> Self {
StreamData::Raw(value)
}
}
impl From<Result<Vec<u8>, ShellError>> for StreamData {
fn from(value: Result<Vec<u8>, ShellError>) -> Self {
value.map_err(LabeledError::from).into()
}
}
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>, LabeledError> {
type Error = ShellError;
fn try_from(data: StreamData) -> Result<Result<Vec<u8>, LabeledError>, ShellError> {
match data {
StreamData::Raw(value) => Ok(value),
StreamData::List(_) => Err(ShellError::PluginFailedToDecode {
msg: "expected raw stream data, found list data".into(),
}),
}
}
}
impl TryFrom<StreamData> for Result<Vec<u8>, ShellError> {
type Error = ShellError;
fn try_from(value: StreamData) -> Result<Result<Vec<u8>, ShellError>, ShellError> {
Result::<Vec<u8>, LabeledError>::try_from(value).map(|res| res.map_err(ShellError::from))
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum StreamMessage {
Data(StreamId, StreamData),
End(StreamId),
Drop(StreamId),
Ack(StreamId),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum PluginCallResponse<D> {
Ok,
Error(ShellError),
Metadata(PluginMetadata),
Signature(Vec<PluginSignature>),
Ordering(Option<Ordering>),
CompletionItems(Option<Vec<DynamicSuggestion>>),
PipelineData(D),
}
impl<D> PluginCallResponse<D> {
pub fn map_data<T>(
self,
f: impl FnOnce(D) -> Result<T, ShellError>,
) -> Result<PluginCallResponse<T>, ShellError> {
Ok(match self {
PluginCallResponse::Ok => PluginCallResponse::Ok,
PluginCallResponse::Error(err) => PluginCallResponse::Error(err),
PluginCallResponse::Metadata(meta) => PluginCallResponse::Metadata(meta),
PluginCallResponse::Signature(sigs) => PluginCallResponse::Signature(sigs),
PluginCallResponse::Ordering(ordering) => PluginCallResponse::Ordering(ordering),
PluginCallResponse::CompletionItems(items) => {
PluginCallResponse::CompletionItems(items)
}
PluginCallResponse::PipelineData(input) => PluginCallResponse::PipelineData(f(input)?),
})
}
}
impl PluginCallResponse<PipelineDataHeader> {
pub fn value(value: Value) -> PluginCallResponse<PipelineDataHeader> {
if value.is_nothing() {
PluginCallResponse::PipelineData(PipelineDataHeader::Empty)
} else {
PluginCallResponse::PipelineData(PipelineDataHeader::value(value))
}
}
}
impl PluginCallResponse<PipelineData> {
pub fn has_stream(&self) -> bool {
match self {
PluginCallResponse::PipelineData(data) => match data {
PipelineData::Empty => false,
PipelineData::Value(..) => false,
PipelineData::ListStream(..) => true,
PipelineData::ByteStream(..) => true,
},
_ => false,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum PluginOption {
GcDisabled(bool),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Ordering {
Less,
Equal,
Greater,
}
impl From<std::cmp::Ordering> for Ordering {
fn from(value: std::cmp::Ordering) -> Self {
match value {
std::cmp::Ordering::Less => Ordering::Less,
std::cmp::Ordering::Equal => Ordering::Equal,
std::cmp::Ordering::Greater => Ordering::Greater,
}
}
}
impl From<Ordering> for std::cmp::Ordering {
fn from(value: Ordering) -> Self {
match value {
Ordering::Less => std::cmp::Ordering::Less,
Ordering::Equal => std::cmp::Ordering::Equal,
Ordering::Greater => std::cmp::Ordering::Greater,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum PluginOutput {
Hello(ProtocolInfo),
Option(PluginOption),
CallResponse(PluginCallId, PluginCallResponse<PipelineDataHeader>),
EngineCall {
context: PluginCallId,
id: EngineCallId,
call: EngineCall<PipelineDataHeader>,
},
Data(StreamId, StreamData),
End(StreamId),
Drop(StreamId),
Ack(StreamId),
}
impl TryFrom<PluginOutput> for StreamMessage {
type Error = PluginOutput;
fn try_from(msg: PluginOutput) -> Result<StreamMessage, PluginOutput> {
match msg {
PluginOutput::Data(id, data) => Ok(StreamMessage::Data(id, data)),
PluginOutput::End(id) => Ok(StreamMessage::End(id)),
PluginOutput::Drop(id) => Ok(StreamMessage::Drop(id)),
PluginOutput::Ack(id) => Ok(StreamMessage::Ack(id)),
_ => Err(msg),
}
}
}
impl From<StreamMessage> for PluginOutput {
fn from(stream_msg: StreamMessage) -> PluginOutput {
match stream_msg {
StreamMessage::Data(id, data) => PluginOutput::Data(id, data),
StreamMessage::End(id) => PluginOutput::End(id),
StreamMessage::Drop(id) => PluginOutput::Drop(id),
StreamMessage::Ack(id) => PluginOutput::Ack(id),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum EngineCall<D> {
GetConfig,
GetPluginConfig,
GetEnvVar(String),
GetEnvVars,
GetCurrentDir,
AddEnvVar(String, Value),
GetHelp,
EnterForeground,
LeaveForeground,
GetSpanContents(Span),
EvalClosure {
closure: Spanned<Closure>,
positional: Vec<Value>,
input: D,
redirect_stdout: bool,
redirect_stderr: bool,
},
FindDecl(String),
GetBlockIR(BlockId),
CallDecl {
decl_id: DeclId,
call: EvaluatedCall,
input: D,
redirect_stdout: bool,
redirect_stderr: bool,
},
}
impl<D> EngineCall<D> {
pub fn name(&self) -> &'static str {
match self {
EngineCall::GetConfig => "GetConfig",
EngineCall::GetPluginConfig => "GetPluginConfig",
EngineCall::GetEnvVar(_) => "GetEnv",
EngineCall::GetEnvVars => "GetEnvs",
EngineCall::GetCurrentDir => "GetCurrentDir",
EngineCall::AddEnvVar(..) => "AddEnvVar",
EngineCall::GetHelp => "GetHelp",
EngineCall::EnterForeground => "EnterForeground",
EngineCall::LeaveForeground => "LeaveForeground",
EngineCall::GetSpanContents(_) => "GetSpanContents",
EngineCall::EvalClosure { .. } => "EvalClosure",
EngineCall::FindDecl(_) => "FindDecl",
EngineCall::GetBlockIR(_) => "GetBlockIR",
EngineCall::CallDecl { .. } => "CallDecl",
}
}
pub fn map_data<T>(
self,
f: impl FnOnce(D) -> Result<T, ShellError>,
) -> Result<EngineCall<T>, ShellError> {
Ok(match self {
EngineCall::GetConfig => EngineCall::GetConfig,
EngineCall::GetPluginConfig => EngineCall::GetPluginConfig,
EngineCall::GetEnvVar(name) => EngineCall::GetEnvVar(name),
EngineCall::GetEnvVars => EngineCall::GetEnvVars,
EngineCall::GetCurrentDir => EngineCall::GetCurrentDir,
EngineCall::AddEnvVar(name, value) => EngineCall::AddEnvVar(name, value),
EngineCall::GetHelp => EngineCall::GetHelp,
EngineCall::EnterForeground => EngineCall::EnterForeground,
EngineCall::LeaveForeground => EngineCall::LeaveForeground,
EngineCall::GetSpanContents(span) => EngineCall::GetSpanContents(span),
EngineCall::EvalClosure {
closure,
positional,
input,
redirect_stdout,
redirect_stderr,
} => EngineCall::EvalClosure {
closure,
positional,
input: f(input)?,
redirect_stdout,
redirect_stderr,
},
EngineCall::FindDecl(name) => EngineCall::FindDecl(name),
EngineCall::GetBlockIR(block_id) => EngineCall::GetBlockIR(block_id),
EngineCall::CallDecl {
decl_id,
call,
input,
redirect_stdout,
redirect_stderr,
} => EngineCall::CallDecl {
decl_id,
call,
input: f(input)?,
redirect_stdout,
redirect_stderr,
},
})
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum EngineCallResponse<D> {
Error(ShellError),
PipelineData(D),
Config(SharedCow<Config>),
ValueMap(HashMap<String, Value>),
Identifier(DeclId),
IrBlock(Box<IrBlock>),
}
impl<D> EngineCallResponse<D> {
pub fn map_data<T>(
self,
f: impl FnOnce(D) -> Result<T, ShellError>,
) -> Result<EngineCallResponse<T>, ShellError> {
Ok(match self {
EngineCallResponse::Error(err) => EngineCallResponse::Error(err),
EngineCallResponse::PipelineData(data) => EngineCallResponse::PipelineData(f(data)?),
EngineCallResponse::Config(config) => EngineCallResponse::Config(config),
EngineCallResponse::ValueMap(map) => EngineCallResponse::ValueMap(map),
EngineCallResponse::Identifier(id) => EngineCallResponse::Identifier(id),
EngineCallResponse::IrBlock(ir) => EngineCallResponse::IrBlock(ir),
})
}
}
impl EngineCallResponse<PipelineData> {
pub fn value(value: Value) -> EngineCallResponse<PipelineData> {
EngineCallResponse::PipelineData(PipelineData::value(value, None))
}
pub const fn empty() -> EngineCallResponse<PipelineData> {
EngineCallResponse::PipelineData(PipelineData::empty())
}
}