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
use itertools::Itertools;
use nu_engine::command_prelude::*;

#[derive(Clone)]
pub struct PluginList;

impl Command for PluginList {
    fn name(&self) -> &str {
        "plugin list"
    }

    fn signature(&self) -> Signature {
        Signature::build("plugin list")
            .input_output_type(
                Type::Nothing,
                Type::Table(
                    [
                        ("name".into(), Type::String),
                        ("is_running".into(), Type::Bool),
                        ("pid".into(), Type::Int),
                        ("filename".into(), Type::String),
                        ("shell".into(), Type::String),
                        ("commands".into(), Type::List(Type::String.into())),
                    ]
                    .into(),
                ),
            )
            .category(Category::Plugin)
    }

    fn usage(&self) -> &str {
        "List installed plugins."
    }

    fn search_terms(&self) -> Vec<&str> {
        vec!["scope"]
    }

    fn examples(&self) -> Vec<nu_protocol::Example> {
        vec![
            Example {
                example: "plugin list",
                description: "List installed plugins.",
                result: Some(Value::test_list(vec![Value::test_record(record! {
                    "name" => Value::test_string("inc"),
                    "is_running" => Value::test_bool(true),
                    "pid" => Value::test_int(106480),
                    "filename" => if cfg!(windows) {
                        Value::test_string(r"C:\nu\plugins\nu_plugin_inc.exe")
                    } else {
                        Value::test_string("/opt/nu/plugins/nu_plugin_inc")
                    },
                    "shell" => Value::test_nothing(),
                    "commands" => Value::test_list(vec![Value::test_string("inc")]),
                })])),
            },
            Example {
                example: "ps | where pid in (plugin list).pid",
                description: "Get process information for running plugins.",
                result: None,
            },
        ]
    }

    fn run(
        &self,
        engine_state: &EngineState,
        _stack: &mut Stack,
        call: &Call,
        _input: PipelineData,
    ) -> Result<PipelineData, ShellError> {
        let head = call.head;

        // Group plugin decls by plugin identity
        let decls = engine_state.plugin_decls().into_group_map_by(|decl| {
            decl.plugin_identity()
                .expect("plugin decl should have identity")
        });

        // Build plugins list
        let list = engine_state.plugins().iter().map(|plugin| {
            // Find commands that belong to the plugin
            let commands = decls.get(plugin.identity())
                .into_iter()
                .flat_map(|decls| {
                    decls.iter().map(|decl| Value::string(decl.name(), head))
                })
                .collect();

            let pid = plugin
                .pid()
                .map(|p| Value::int(p as i64, head))
                .unwrap_or(Value::nothing(head));

            let shell = plugin
                .identity()
                .shell()
                .map(|s| Value::string(s.to_string_lossy(), head))
                .unwrap_or(Value::nothing(head));

            let record = record! {
                "name" => Value::string(plugin.identity().name(), head),
                "is_running" => Value::bool(plugin.is_running(), head),
                "pid" => pid,
                "filename" => Value::string(plugin.identity().filename().to_string_lossy(), head),
                "shell" => shell,
                "commands" => Value::list(commands, head),
            };

            Value::record(record, head)
        }).collect();

        Ok(Value::list(list, head).into_pipeline_data())
    }
}