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
127
128
129
130
131
132
133
134
135
136
use crate::jsonrpc::{send_response, NuCommand};
use nu_errors::ShellError;
use nu_protocol::{CallInfo, ReturnValue, Signature, Value};
use std::io;

/// The `Plugin` trait defines the API which plugins may use to "hook" into nushell.
pub trait Plugin {
    /// The `config` method is used to configure a plugin's user interface / signature.
    ///
    /// This is where the "name" of the plugin (ex `fetch`), description, any required/optional fields, and flags
    /// can be defined. This information will displayed in nushell when running help <plugin name>
    fn config(&mut self) -> Result<Signature, ShellError>;

    /// `begin_filter` is the first method to be called if the `Signature` of the plugin is configured to be filterable.
    /// Any setup required for the plugin such as parsing arguments from `CallInfo` or initializing data structures
    /// can be done here. The `CallInfo` parameter will contain data configured in the `config` method of the Plugin trait.
    fn begin_filter(&mut self, _call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
        Ok(vec![])
    }

    /// `filter` is called for every `Value` that is processed by the plugin.
    /// This method requires the plugin `Signature` to be configured as filterable.
    fn filter(&mut self, _input: Value) -> Result<Vec<ReturnValue>, ShellError> {
        Ok(vec![])
    }

    /// `end_filter` is the last method to be called by the plugin after all `Value`s are processed by the plugin.
    /// This method requires the plugin `Signature` to be configured as filterable.
    fn end_filter(&mut self) -> Result<Vec<ReturnValue>, ShellError> {
        Ok(vec![])
    }

    /// `sink` consumes the `Value`s that are passed in, preventing further processing.
    /// This method requires the plugin `Signature` to be configured without filtering.
    fn sink(&mut self, _call_info: CallInfo, _input: Vec<Value>) {}

    fn quit(&mut self) {}
}

pub fn serve_plugin(plugin: &mut dyn Plugin) {
    let mut args = std::env::args();
    if args.len() > 1 {
        let input = args.nth(1);

        let input = match input {
            Some(arg) => std::fs::read_to_string(arg),
            None => {
                send_response(ShellError::untagged_runtime_error("No input given."));
                return;
            }
        };

        if let Ok(input) = input {
            let command = serde_json::from_str::<NuCommand>(&input);
            match command {
                Ok(NuCommand::config) => {
                    send_response(plugin.config());
                    return;
                }
                Ok(NuCommand::begin_filter { params }) => {
                    send_response(plugin.begin_filter(params));
                }
                Ok(NuCommand::filter { params }) => {
                    send_response(plugin.filter(params));
                }
                Ok(NuCommand::end_filter) => {
                    send_response(plugin.end_filter());
                    return;
                }

                Ok(NuCommand::sink { params }) => {
                    plugin.sink(params.0, params.1);
                    return;
                }
                Ok(NuCommand::quit) => {
                    plugin.quit();
                    return;
                }
                e => {
                    send_response(ShellError::untagged_runtime_error(format!(
                        "Could not handle plugin message: {} {:?}",
                        input, e
                    )));
                    return;
                }
            }
        }
    } else {
        loop {
            let mut input = String::new();
            match io::stdin().read_line(&mut input) {
                Ok(_) => {
                    let command = serde_json::from_str::<NuCommand>(&input);
                    match command {
                        Ok(NuCommand::config) => {
                            send_response(plugin.config());
                            break;
                        }
                        Ok(NuCommand::begin_filter { params }) => {
                            send_response(plugin.begin_filter(params));
                        }
                        Ok(NuCommand::filter { params }) => {
                            send_response(plugin.filter(params));
                        }
                        Ok(NuCommand::end_filter) => {
                            send_response(plugin.end_filter());
                            break;
                        }
                        Ok(NuCommand::sink { params }) => {
                            plugin.sink(params.0, params.1);
                            break;
                        }
                        Ok(NuCommand::quit) => {
                            plugin.quit();
                            break;
                        }
                        e => {
                            send_response(ShellError::untagged_runtime_error(format!(
                                "Could not handle plugin message: {} {:?}",
                                input, e
                            )));
                            break;
                        }
                    }
                }
                e => {
                    send_response(ShellError::untagged_runtime_error(format!(
                        "Could not handle plugin message: {:?}",
                        e,
                    )));
                    break;
                }
            }
        }
    }
}