nushell/crates/nu-plugin/src/plugin.rs
2021-01-02 17:24:32 +13:00

137 lines
5.4 KiB
Rust

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;
}
}
}
}
}