1mod evaluated_call;
13mod plugin_custom_value;
14mod protocol_info;
15
16#[cfg(test)]
17mod tests;
18
19#[doc(hidden)]
22pub mod test_util;
23
24use nu_protocol::{
25 ByteStreamType, Config, DeclId, DynamicSuggestion, LabeledError, PipelineData,
26 PipelineMetadata, PluginMetadata, PluginSignature, ShellError, SignalAction, Span, Spanned,
27 Value, ast,
28 ast::Operator,
29 casing::Casing,
30 engine::{ArgType, Closure},
31};
32use nu_utils::SharedCow;
33use serde::{Deserialize, Serialize};
34use std::{collections::HashMap, path::PathBuf};
35
36pub use evaluated_call::EvaluatedCall;
37pub use plugin_custom_value::PluginCustomValue;
38#[allow(unused_imports)] pub use protocol_info::{Feature, Protocol, ProtocolInfo};
40
41pub type StreamId = usize;
43
44pub type PluginCallId = usize;
46
47pub type EngineCallId = usize;
49
50#[derive(Serialize, Deserialize, Debug, Clone)]
54pub struct CallInfo<D> {
55 pub name: String,
57 pub call: EvaluatedCall,
59 pub input: D,
61}
62
63#[derive(Serialize, Deserialize, Debug, Clone)]
64pub enum GetCompletionArgType {
65 Flag(String),
66 Positional(usize),
67}
68
69impl<'a> From<GetCompletionArgType> for ArgType<'a> {
70 fn from(value: GetCompletionArgType) -> Self {
71 match value {
72 GetCompletionArgType::Flag(flag_name) => {
73 ArgType::Flag(std::borrow::Cow::from(flag_name))
74 }
75 GetCompletionArgType::Positional(idx) => ArgType::Positional(idx),
76 }
77 }
78}
79
80#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
83pub struct DynamicCompletionCall {
84 pub call: ast::Call,
86 pub strip: bool,
88 pub pos: usize,
90}
91
92#[derive(Serialize, Deserialize, Debug, Clone)]
94pub struct GetCompletionInfo {
95 pub name: String,
97 pub arg_type: GetCompletionArgType,
99 pub call: DynamicCompletionCall,
101}
102
103impl<D> CallInfo<D> {
104 pub fn map_data<T>(
106 self,
107 f: impl FnOnce(D) -> Result<T, ShellError>,
108 ) -> Result<CallInfo<T>, ShellError> {
109 Ok(CallInfo {
110 name: self.name,
111 call: self.call,
112 input: f(self.input)?,
113 })
114 }
115}
116
117#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
121pub enum PipelineDataHeader {
122 Empty,
124 Value(Value, Option<PipelineMetadata>),
126 ListStream(ListStreamInfo),
130 ByteStream(ByteStreamInfo),
134}
135
136impl PipelineDataHeader {
137 pub fn stream_id(&self) -> Option<StreamId> {
139 match self {
140 PipelineDataHeader::Empty => None,
141 PipelineDataHeader::Value(_, _) => None,
142 PipelineDataHeader::ListStream(info) => Some(info.id),
143 PipelineDataHeader::ByteStream(info) => Some(info.id),
144 }
145 }
146
147 pub fn value(value: Value) -> Self {
148 PipelineDataHeader::Value(value, None)
149 }
150
151 pub fn list_stream(info: ListStreamInfo) -> Self {
152 PipelineDataHeader::ListStream(info)
153 }
154
155 pub fn byte_stream(info: ByteStreamInfo) -> Self {
156 PipelineDataHeader::ByteStream(info)
157 }
158}
159
160#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
162pub struct ListStreamInfo {
163 pub id: StreamId,
164 pub span: Span,
165 pub metadata: Option<PipelineMetadata>,
166}
167
168impl ListStreamInfo {
169 pub fn new(id: StreamId, span: Span) -> Self {
171 ListStreamInfo {
172 id,
173 span,
174 metadata: None,
175 }
176 }
177}
178
179#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
181pub struct ByteStreamInfo {
182 pub id: StreamId,
183 pub span: Span,
184 #[serde(rename = "type")]
185 pub type_: ByteStreamType,
186 pub metadata: Option<PipelineMetadata>,
187}
188
189impl ByteStreamInfo {
190 pub fn new(id: StreamId, span: Span, type_: ByteStreamType) -> Self {
192 ByteStreamInfo {
193 id,
194 span,
195 type_,
196 metadata: None,
197 }
198 }
199}
200
201#[derive(Serialize, Deserialize, Debug, Clone)]
203pub enum PluginCall<D> {
204 Metadata,
205 Signature,
206 Run(CallInfo<D>),
207 GetCompletion(GetCompletionInfo),
208 CustomValueOp(Spanned<PluginCustomValue>, CustomValueOp),
209}
210
211impl<D> PluginCall<D> {
212 pub fn map_data<T>(
215 self,
216 f: impl FnOnce(D) -> Result<T, ShellError>,
217 ) -> Result<PluginCall<T>, ShellError> {
218 Ok(match self {
219 PluginCall::Metadata => PluginCall::Metadata,
220 PluginCall::Signature => PluginCall::Signature,
221 PluginCall::GetCompletion(flag_name) => PluginCall::GetCompletion(flag_name),
222 PluginCall::Run(call) => PluginCall::Run(call.map_data(f)?),
223 PluginCall::CustomValueOp(custom_value, op) => {
224 PluginCall::CustomValueOp(custom_value, op)
225 }
226 })
227 }
228
229 pub fn span(&self) -> Option<Span> {
231 match self {
232 PluginCall::Metadata => None,
233 PluginCall::Signature => None,
234 PluginCall::GetCompletion(_) => None,
235 PluginCall::Run(CallInfo { call, .. }) => Some(call.head),
236 PluginCall::CustomValueOp(val, _) => Some(val.span),
237 }
238 }
239}
240
241#[derive(Serialize, Deserialize, Debug, Clone)]
243pub enum CustomValueOp {
244 ToBaseValue,
246 FollowPathInt {
248 index: Spanned<usize>,
249 optional: bool,
250 },
251 FollowPathString {
253 column_name: Spanned<String>,
254 optional: bool,
255 casing: Casing,
256 },
257 PartialCmp(Value),
259 Operation(Spanned<Operator>, Value),
261 Save {
263 path: Spanned<PathBuf>,
264 save_call_span: Span,
265 },
266 Dropped,
269}
270
271impl CustomValueOp {
272 pub fn name(&self) -> &'static str {
274 match self {
275 CustomValueOp::ToBaseValue => "to_base_value",
276 CustomValueOp::FollowPathInt { .. } => "follow_path_int",
277 CustomValueOp::FollowPathString { .. } => "follow_path_string",
278 CustomValueOp::PartialCmp(_) => "partial_cmp",
279 CustomValueOp::Operation(_, _) => "operation",
280 CustomValueOp::Save { .. } => "save",
281 CustomValueOp::Dropped => "dropped",
282 }
283 }
284}
285
286#[derive(Serialize, Deserialize, Debug, Clone)]
288pub enum PluginInput {
289 Hello(ProtocolInfo),
291 Call(PluginCallId, PluginCall<PipelineDataHeader>),
294 Goodbye,
297 EngineCallResponse(EngineCallId, EngineCallResponse<PipelineDataHeader>),
300 Data(StreamId, StreamData),
302 End(StreamId),
304 Drop(StreamId),
306 Ack(StreamId),
308 Signal(SignalAction),
310}
311
312impl TryFrom<PluginInput> for StreamMessage {
313 type Error = PluginInput;
314
315 fn try_from(msg: PluginInput) -> Result<StreamMessage, PluginInput> {
316 match msg {
317 PluginInput::Data(id, data) => Ok(StreamMessage::Data(id, data)),
318 PluginInput::End(id) => Ok(StreamMessage::End(id)),
319 PluginInput::Drop(id) => Ok(StreamMessage::Drop(id)),
320 PluginInput::Ack(id) => Ok(StreamMessage::Ack(id)),
321 _ => Err(msg),
322 }
323 }
324}
325
326impl From<StreamMessage> for PluginInput {
327 fn from(stream_msg: StreamMessage) -> PluginInput {
328 match stream_msg {
329 StreamMessage::Data(id, data) => PluginInput::Data(id, data),
330 StreamMessage::End(id) => PluginInput::End(id),
331 StreamMessage::Drop(id) => PluginInput::Drop(id),
332 StreamMessage::Ack(id) => PluginInput::Ack(id),
333 }
334 }
335}
336
337#[derive(Serialize, Deserialize, Debug, Clone)]
339pub enum StreamData {
340 List(Value),
341 Raw(Result<Vec<u8>, LabeledError>),
342}
343
344impl From<Value> for StreamData {
345 fn from(value: Value) -> Self {
346 StreamData::List(value)
347 }
348}
349
350impl From<Result<Vec<u8>, LabeledError>> for StreamData {
351 fn from(value: Result<Vec<u8>, LabeledError>) -> Self {
352 StreamData::Raw(value)
353 }
354}
355
356impl From<Result<Vec<u8>, ShellError>> for StreamData {
357 fn from(value: Result<Vec<u8>, ShellError>) -> Self {
358 value.map_err(LabeledError::from).into()
359 }
360}
361
362impl TryFrom<StreamData> for Value {
363 type Error = ShellError;
364
365 fn try_from(data: StreamData) -> Result<Value, ShellError> {
366 match data {
367 StreamData::List(value) => Ok(value),
368 StreamData::Raw(_) => Err(ShellError::PluginFailedToDecode {
369 msg: "expected list stream data, found raw data".into(),
370 }),
371 }
372 }
373}
374
375impl TryFrom<StreamData> for Result<Vec<u8>, LabeledError> {
376 type Error = ShellError;
377
378 fn try_from(data: StreamData) -> Result<Result<Vec<u8>, LabeledError>, ShellError> {
379 match data {
380 StreamData::Raw(value) => Ok(value),
381 StreamData::List(_) => Err(ShellError::PluginFailedToDecode {
382 msg: "expected raw stream data, found list data".into(),
383 }),
384 }
385 }
386}
387
388impl TryFrom<StreamData> for Result<Vec<u8>, ShellError> {
389 type Error = ShellError;
390
391 fn try_from(value: StreamData) -> Result<Result<Vec<u8>, ShellError>, ShellError> {
392 Result::<Vec<u8>, LabeledError>::try_from(value).map(|res| res.map_err(ShellError::from))
393 }
394}
395
396#[derive(Serialize, Deserialize, Debug, Clone)]
398pub enum StreamMessage {
399 Data(StreamId, StreamData),
401 End(StreamId),
403 Drop(StreamId),
406 Ack(StreamId),
409}
410
411#[derive(Serialize, Deserialize, Debug, Clone)]
413pub enum PluginCallResponse<D> {
414 Ok,
415 Error(LabeledError),
416 Metadata(PluginMetadata),
417 Signature(Vec<PluginSignature>),
418 Ordering(Option<Ordering>),
419 CompletionItems(Option<Vec<DynamicSuggestion>>),
420 PipelineData(D),
421}
422
423impl<D> PluginCallResponse<D> {
424 pub fn map_data<T>(
427 self,
428 f: impl FnOnce(D) -> Result<T, ShellError>,
429 ) -> Result<PluginCallResponse<T>, ShellError> {
430 Ok(match self {
431 PluginCallResponse::Ok => PluginCallResponse::Ok,
432 PluginCallResponse::Error(err) => PluginCallResponse::Error(err),
433 PluginCallResponse::Metadata(meta) => PluginCallResponse::Metadata(meta),
434 PluginCallResponse::Signature(sigs) => PluginCallResponse::Signature(sigs),
435 PluginCallResponse::Ordering(ordering) => PluginCallResponse::Ordering(ordering),
436 PluginCallResponse::CompletionItems(items) => {
437 PluginCallResponse::CompletionItems(items)
438 }
439 PluginCallResponse::PipelineData(input) => PluginCallResponse::PipelineData(f(input)?),
440 })
441 }
442}
443
444impl PluginCallResponse<PipelineDataHeader> {
445 pub fn value(value: Value) -> PluginCallResponse<PipelineDataHeader> {
447 if value.is_nothing() {
448 PluginCallResponse::PipelineData(PipelineDataHeader::Empty)
449 } else {
450 PluginCallResponse::PipelineData(PipelineDataHeader::value(value))
451 }
452 }
453}
454
455impl PluginCallResponse<PipelineData> {
456 pub fn has_stream(&self) -> bool {
458 match self {
459 PluginCallResponse::PipelineData(data) => match data {
460 PipelineData::Empty => false,
461 PipelineData::Value(..) => false,
462 PipelineData::ListStream(..) => true,
463 PipelineData::ByteStream(..) => true,
464 },
465 _ => false,
466 }
467 }
468}
469
470#[derive(Serialize, Deserialize, Debug, Clone)]
472pub enum PluginOption {
473 GcDisabled(bool),
478}
479
480#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
482pub enum Ordering {
483 Less,
484 Equal,
485 Greater,
486}
487
488impl From<std::cmp::Ordering> for Ordering {
489 fn from(value: std::cmp::Ordering) -> Self {
490 match value {
491 std::cmp::Ordering::Less => Ordering::Less,
492 std::cmp::Ordering::Equal => Ordering::Equal,
493 std::cmp::Ordering::Greater => Ordering::Greater,
494 }
495 }
496}
497
498impl From<Ordering> for std::cmp::Ordering {
499 fn from(value: Ordering) -> Self {
500 match value {
501 Ordering::Less => std::cmp::Ordering::Less,
502 Ordering::Equal => std::cmp::Ordering::Equal,
503 Ordering::Greater => std::cmp::Ordering::Greater,
504 }
505 }
506}
507
508#[derive(Serialize, Deserialize, Debug, Clone)]
510pub enum PluginOutput {
511 Hello(ProtocolInfo),
513 Option(PluginOption),
515 CallResponse(PluginCallId, PluginCallResponse<PipelineDataHeader>),
518 EngineCall {
521 context: PluginCallId,
523 id: EngineCallId,
525 call: EngineCall<PipelineDataHeader>,
526 },
527 Data(StreamId, StreamData),
529 End(StreamId),
531 Drop(StreamId),
533 Ack(StreamId),
535}
536
537impl TryFrom<PluginOutput> for StreamMessage {
538 type Error = PluginOutput;
539
540 fn try_from(msg: PluginOutput) -> Result<StreamMessage, PluginOutput> {
541 match msg {
542 PluginOutput::Data(id, data) => Ok(StreamMessage::Data(id, data)),
543 PluginOutput::End(id) => Ok(StreamMessage::End(id)),
544 PluginOutput::Drop(id) => Ok(StreamMessage::Drop(id)),
545 PluginOutput::Ack(id) => Ok(StreamMessage::Ack(id)),
546 _ => Err(msg),
547 }
548 }
549}
550
551impl From<StreamMessage> for PluginOutput {
552 fn from(stream_msg: StreamMessage) -> PluginOutput {
553 match stream_msg {
554 StreamMessage::Data(id, data) => PluginOutput::Data(id, data),
555 StreamMessage::End(id) => PluginOutput::End(id),
556 StreamMessage::Drop(id) => PluginOutput::Drop(id),
557 StreamMessage::Ack(id) => PluginOutput::Ack(id),
558 }
559 }
560}
561
562#[derive(Serialize, Deserialize, Debug, Clone)]
566pub enum EngineCall<D> {
567 GetConfig,
569 GetPluginConfig,
571 GetEnvVar(String),
573 GetEnvVars,
575 GetCurrentDir,
577 AddEnvVar(String, Value),
579 GetHelp,
581 EnterForeground,
583 LeaveForeground,
585 GetSpanContents(Span),
587 EvalClosure {
589 closure: Spanned<Closure>,
593 positional: Vec<Value>,
595 input: D,
597 redirect_stdout: bool,
599 redirect_stderr: bool,
601 },
602 FindDecl(String),
604 CallDecl {
606 decl_id: DeclId,
608 call: EvaluatedCall,
610 input: D,
612 redirect_stdout: bool,
614 redirect_stderr: bool,
616 },
617}
618
619impl<D> EngineCall<D> {
620 pub fn name(&self) -> &'static str {
622 match self {
623 EngineCall::GetConfig => "GetConfig",
624 EngineCall::GetPluginConfig => "GetPluginConfig",
625 EngineCall::GetEnvVar(_) => "GetEnv",
626 EngineCall::GetEnvVars => "GetEnvs",
627 EngineCall::GetCurrentDir => "GetCurrentDir",
628 EngineCall::AddEnvVar(..) => "AddEnvVar",
629 EngineCall::GetHelp => "GetHelp",
630 EngineCall::EnterForeground => "EnterForeground",
631 EngineCall::LeaveForeground => "LeaveForeground",
632 EngineCall::GetSpanContents(_) => "GetSpanContents",
633 EngineCall::EvalClosure { .. } => "EvalClosure",
634 EngineCall::FindDecl(_) => "FindDecl",
635 EngineCall::CallDecl { .. } => "CallDecl",
636 }
637 }
638
639 pub fn map_data<T>(
642 self,
643 f: impl FnOnce(D) -> Result<T, ShellError>,
644 ) -> Result<EngineCall<T>, ShellError> {
645 Ok(match self {
646 EngineCall::GetConfig => EngineCall::GetConfig,
647 EngineCall::GetPluginConfig => EngineCall::GetPluginConfig,
648 EngineCall::GetEnvVar(name) => EngineCall::GetEnvVar(name),
649 EngineCall::GetEnvVars => EngineCall::GetEnvVars,
650 EngineCall::GetCurrentDir => EngineCall::GetCurrentDir,
651 EngineCall::AddEnvVar(name, value) => EngineCall::AddEnvVar(name, value),
652 EngineCall::GetHelp => EngineCall::GetHelp,
653 EngineCall::EnterForeground => EngineCall::EnterForeground,
654 EngineCall::LeaveForeground => EngineCall::LeaveForeground,
655 EngineCall::GetSpanContents(span) => EngineCall::GetSpanContents(span),
656 EngineCall::EvalClosure {
657 closure,
658 positional,
659 input,
660 redirect_stdout,
661 redirect_stderr,
662 } => EngineCall::EvalClosure {
663 closure,
664 positional,
665 input: f(input)?,
666 redirect_stdout,
667 redirect_stderr,
668 },
669 EngineCall::FindDecl(name) => EngineCall::FindDecl(name),
670 EngineCall::CallDecl {
671 decl_id,
672 call,
673 input,
674 redirect_stdout,
675 redirect_stderr,
676 } => EngineCall::CallDecl {
677 decl_id,
678 call,
679 input: f(input)?,
680 redirect_stdout,
681 redirect_stderr,
682 },
683 })
684 }
685}
686
687#[derive(Serialize, Deserialize, Debug, Clone)]
690pub enum EngineCallResponse<D> {
691 Error(ShellError),
692 PipelineData(D),
693 Config(SharedCow<Config>),
694 ValueMap(HashMap<String, Value>),
695 Identifier(DeclId),
696}
697
698impl<D> EngineCallResponse<D> {
699 pub fn map_data<T>(
702 self,
703 f: impl FnOnce(D) -> Result<T, ShellError>,
704 ) -> Result<EngineCallResponse<T>, ShellError> {
705 Ok(match self {
706 EngineCallResponse::Error(err) => EngineCallResponse::Error(err),
707 EngineCallResponse::PipelineData(data) => EngineCallResponse::PipelineData(f(data)?),
708 EngineCallResponse::Config(config) => EngineCallResponse::Config(config),
709 EngineCallResponse::ValueMap(map) => EngineCallResponse::ValueMap(map),
710 EngineCallResponse::Identifier(id) => EngineCallResponse::Identifier(id),
711 })
712 }
713}
714
715impl EngineCallResponse<PipelineData> {
716 pub fn value(value: Value) -> EngineCallResponse<PipelineData> {
718 EngineCallResponse::PipelineData(PipelineData::value(value, None))
719 }
720
721 pub const fn empty() -> EngineCallResponse<PipelineData> {
723 EngineCallResponse::PipelineData(PipelineData::empty())
724 }
725}