nu_plugin_engine/
declaration.rs1use nu_engine::{command_prelude::*, get_eval_expression};
2use nu_plugin_protocol::{
3 CallInfo, DynamicCompletionCall, EvaluatedCall, GetCompletionArgType, GetCompletionInfo,
4};
5use nu_protocol::engine::ArgType;
6use nu_protocol::shell_error::generic::GenericError;
7use nu_protocol::{DynamicCompletionCallRef, DynamicSuggestion};
8use nu_protocol::{PluginIdentity, PluginSignature, engine::CommandType};
9use std::sync::Arc;
10
11use crate::{GetPlugin, PluginExecutionCommandContext, PluginSource};
12
13#[derive(Clone)]
15pub struct PluginDeclaration {
16 name: String,
17 signature: PluginSignature,
18 source: PluginSource,
19}
20
21impl PluginDeclaration {
22 pub fn new(plugin: Arc<dyn GetPlugin>, signature: PluginSignature) -> Self {
23 Self {
24 name: signature.sig.name.clone(),
25 signature,
26 source: PluginSource::new(plugin),
27 }
28 }
29}
30
31impl Command for PluginDeclaration {
32 fn name(&self) -> &str {
33 &self.name
34 }
35
36 fn signature(&self) -> Signature {
37 self.signature.sig.clone()
38 }
39
40 fn description(&self) -> &str {
41 self.signature.sig.description.as_str()
42 }
43
44 fn extra_description(&self) -> &str {
45 self.signature.sig.extra_description.as_str()
46 }
47
48 fn search_terms(&self) -> Vec<&str> {
49 self.signature
50 .sig
51 .search_terms
52 .iter()
53 .map(|term| term.as_str())
54 .collect()
55 }
56
57 fn examples(&self) -> Vec<Example<'_>> {
58 let mut res = vec![];
59 for e in self.signature.examples.iter() {
60 res.push(Example {
61 example: &e.example,
62 description: &e.description,
63 result: e.result.clone(),
64 })
65 }
66 res
67 }
68
69 fn run(
70 &self,
71 engine_state: &EngineState,
72 stack: &mut Stack,
73 call: &Call,
74 input: PipelineData,
75 ) -> Result<PipelineData, ShellError> {
76 let eval_expression = get_eval_expression(engine_state);
77
78 let evaluated_call =
81 EvaluatedCall::try_from_call(call, engine_state, stack, eval_expression)?;
82
83 let engine_config = stack.get_config(engine_state);
85
86 let plugin = self
88 .source
89 .persistent(None)
90 .and_then(|p| {
91 p.set_gc_config(engine_config.plugin_gc.get(p.identity().name()));
93 p.get_plugin(Some((engine_state, stack)))
94 })
95 .map_err(|err| {
96 let decl = engine_state.get_decl(call.decl_id);
97 ShellError::Generic(GenericError::new(
98 format!("Unable to spawn plugin for `{}`", decl.name()),
99 err.to_string(),
100 call.head,
101 ))
102 })?;
103
104 let mut context = PluginExecutionCommandContext::new(
106 self.source.identity.clone(),
107 engine_state,
108 stack,
109 call,
110 );
111
112 plugin.run(
113 CallInfo {
114 name: self.name.clone(),
115 call: evaluated_call,
116 input,
117 },
118 &mut context,
119 )
120 }
121
122 fn command_type(&self) -> CommandType {
123 CommandType::Plugin
124 }
125
126 fn plugin_identity(&self) -> Option<&PluginIdentity> {
127 Some(&self.source.identity)
128 }
129
130 #[expect(deprecated, reason = "internal usage")]
131 fn get_dynamic_completion(
132 &self,
133 engine_state: &EngineState,
134 stack: &mut Stack,
135 call: DynamicCompletionCallRef,
136 arg_type: &ArgType,
137 _experimental: nu_protocol::engine::ExperimentalMarker,
138 ) -> Result<Option<Vec<DynamicSuggestion>>, ShellError> {
139 let engine_config = stack.get_config(engine_state);
141
142 let plugin = self
144 .source
145 .persistent(None)
146 .and_then(|p| {
147 p.set_gc_config(engine_config.plugin_gc.get(p.identity().name()));
149 p.get_plugin(Some((engine_state, stack)))
150 })
151 .map_err(|err| {
152 ShellError::Generic(GenericError::new_internal(
153 "failed to get custom completion",
154 err.to_string(),
155 ))
156 })?;
157
158 let arg_info = match arg_type {
159 ArgType::Flag(flag_name) => GetCompletionArgType::Flag(flag_name.to_string()),
160 ArgType::Positional(index) => GetCompletionArgType::Positional(*index),
161 };
162 plugin.get_dynamic_completion(GetCompletionInfo {
163 name: self.name.clone(),
164 arg_type: arg_info,
165 call: DynamicCompletionCall {
166 call: call.call.clone(),
167 strip: call.strip,
168 pos: call.pos,
169 },
170 })
171 }
172}