nu_plugin_engine/
declaration.rs

1use nu_engine::{command_prelude::*, get_eval_expression};
2use nu_plugin_protocol::{CallInfo, EvaluatedCall};
3use nu_protocol::{PluginIdentity, PluginSignature, engine::CommandType};
4use std::sync::Arc;
5
6use crate::{GetPlugin, PluginExecutionCommandContext, PluginSource};
7
8/// The command declaration proxy used within the engine for all plugin commands.
9#[derive(Clone)]
10pub struct PluginDeclaration {
11    name: String,
12    signature: PluginSignature,
13    source: PluginSource,
14}
15
16impl PluginDeclaration {
17    pub fn new(plugin: Arc<dyn GetPlugin>, signature: PluginSignature) -> Self {
18        Self {
19            name: signature.sig.name.clone(),
20            signature,
21            source: PluginSource::new(plugin),
22        }
23    }
24}
25
26impl Command for PluginDeclaration {
27    fn name(&self) -> &str {
28        &self.name
29    }
30
31    fn signature(&self) -> Signature {
32        self.signature.sig.clone()
33    }
34
35    fn description(&self) -> &str {
36        self.signature.sig.description.as_str()
37    }
38
39    fn extra_description(&self) -> &str {
40        self.signature.sig.extra_description.as_str()
41    }
42
43    fn search_terms(&self) -> Vec<&str> {
44        self.signature
45            .sig
46            .search_terms
47            .iter()
48            .map(|term| term.as_str())
49            .collect()
50    }
51
52    fn examples(&self) -> Vec<Example> {
53        let mut res = vec![];
54        for e in self.signature.examples.iter() {
55            res.push(Example {
56                example: &e.example,
57                description: &e.description,
58                result: e.result.clone(),
59            })
60        }
61        res
62    }
63
64    fn run(
65        &self,
66        engine_state: &EngineState,
67        stack: &mut Stack,
68        call: &Call,
69        input: PipelineData,
70    ) -> Result<PipelineData, ShellError> {
71        let eval_expression = get_eval_expression(engine_state);
72
73        // Create the EvaluatedCall to send to the plugin first - it's best for this to fail early,
74        // before we actually try to run the plugin command
75        let evaluated_call =
76            EvaluatedCall::try_from_call(call, engine_state, stack, eval_expression)?;
77
78        // Get the engine config
79        let engine_config = stack.get_config(engine_state);
80
81        // Get, or start, the plugin.
82        let plugin = self
83            .source
84            .persistent(None)
85            .and_then(|p| {
86                // Set the garbage collector config from the local config before running
87                p.set_gc_config(engine_config.plugin_gc.get(p.identity().name()));
88                p.get_plugin(Some((engine_state, stack)))
89            })
90            .map_err(|err| {
91                let decl = engine_state.get_decl(call.decl_id);
92                ShellError::GenericError {
93                    error: format!("Unable to spawn plugin for `{}`", decl.name()),
94                    msg: err.to_string(),
95                    span: Some(call.head),
96                    help: None,
97                    inner: vec![],
98                }
99            })?;
100
101        // Create the context to execute in - this supports engine calls and custom values
102        let mut context = PluginExecutionCommandContext::new(
103            self.source.identity.clone(),
104            engine_state,
105            stack,
106            call,
107        );
108
109        plugin.run(
110            CallInfo {
111                name: self.name.clone(),
112                call: evaluated_call,
113                input,
114            },
115            &mut context,
116        )
117    }
118
119    fn command_type(&self) -> CommandType {
120        CommandType::Plugin
121    }
122
123    fn plugin_identity(&self) -> Option<&PluginIdentity> {
124        Some(&self.source.identity)
125    }
126}