nu_plugin_engine/
declaration.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use nu_engine::{command_prelude::*, get_eval_expression};
use nu_plugin_protocol::{CallInfo, EvaluatedCall};
use nu_protocol::{engine::CommandType, PluginIdentity, PluginSignature};
use std::sync::Arc;

use crate::{GetPlugin, PluginExecutionCommandContext, PluginSource};

/// The command declaration proxy used within the engine for all plugin commands.
#[derive(Clone)]
pub struct PluginDeclaration {
    name: String,
    signature: PluginSignature,
    source: PluginSource,
}

impl PluginDeclaration {
    pub fn new(plugin: Arc<dyn GetPlugin>, signature: PluginSignature) -> Self {
        Self {
            name: signature.sig.name.clone(),
            signature,
            source: PluginSource::new(plugin),
        }
    }
}

impl Command for PluginDeclaration {
    fn name(&self) -> &str {
        &self.name
    }

    fn signature(&self) -> Signature {
        self.signature.sig.clone()
    }

    fn description(&self) -> &str {
        self.signature.sig.description.as_str()
    }

    fn extra_description(&self) -> &str {
        self.signature.sig.extra_description.as_str()
    }

    fn search_terms(&self) -> Vec<&str> {
        self.signature
            .sig
            .search_terms
            .iter()
            .map(|term| term.as_str())
            .collect()
    }

    fn examples(&self) -> Vec<Example> {
        let mut res = vec![];
        for e in self.signature.examples.iter() {
            res.push(Example {
                example: &e.example,
                description: &e.description,
                result: e.result.clone(),
            })
        }
        res
    }

    fn run(
        &self,
        engine_state: &EngineState,
        stack: &mut Stack,
        call: &Call,
        input: PipelineData,
    ) -> Result<PipelineData, ShellError> {
        let eval_expression = get_eval_expression(engine_state);

        // Create the EvaluatedCall to send to the plugin first - it's best for this to fail early,
        // before we actually try to run the plugin command
        let evaluated_call =
            EvaluatedCall::try_from_call(call, engine_state, stack, eval_expression)?;

        // Get the engine config
        let engine_config = stack.get_config(engine_state);

        // Get, or start, the plugin.
        let plugin = self
            .source
            .persistent(None)
            .and_then(|p| {
                // Set the garbage collector config from the local config before running
                p.set_gc_config(engine_config.plugin_gc.get(p.identity().name()));
                p.get_plugin(Some((engine_state, stack)))
            })
            .map_err(|err| {
                let decl = engine_state.get_decl(call.decl_id);
                ShellError::GenericError {
                    error: format!("Unable to spawn plugin for `{}`", decl.name()),
                    msg: err.to_string(),
                    span: Some(call.head),
                    help: None,
                    inner: vec![],
                }
            })?;

        // Create the context to execute in - this supports engine calls and custom values
        let mut context = PluginExecutionCommandContext::new(
            self.source.identity.clone(),
            engine_state,
            stack,
            call,
        );

        plugin.run(
            CallInfo {
                name: self.name.clone(),
                call: evaluated_call,
                input,
            },
            &mut context,
        )
    }

    fn command_type(&self) -> CommandType {
        CommandType::Plugin
    }

    fn plugin_identity(&self) -> Option<&PluginIdentity> {
        Some(&self.source.identity)
    }
}