forked from extern/nushell
This commit extracts five new crates: - nu-source, which contains the core source-code handling logic in Nu, including Text, Span, and also the pretty.rs-based debug logic - nu-parser, which is the parser and expander logic - nu-protocol, which is the bulk of the types and basic conveniences used by plugins - nu-errors, which contains ShellError, ParseError and error handling conveniences - nu-textview, which is the textview plugin extracted into a crate One of the major consequences of this refactor is that it's no longer possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so a lot of types became more concrete (Value became a concrete type instead of Spanned<Value>, for example). This also turned a number of inherent methods in the main nu crate into plain functions (impl Value {} became a bunch of functions in the `value` namespace in `crate::data::value`).
163 lines
5.1 KiB
Rust
163 lines
5.1 KiB
Rust
use crate::call_info::CallInfo;
|
|
use crate::return_value::ReturnValue;
|
|
use crate::signature::Signature;
|
|
use crate::value::Value;
|
|
use nu_errors::ShellError;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::io;
|
|
|
|
pub trait Plugin {
|
|
fn config(&mut self) -> Result<Signature, ShellError>;
|
|
|
|
fn begin_filter(&mut self, _call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
|
|
Ok(vec![])
|
|
}
|
|
|
|
fn filter(&mut self, _input: Value) -> Result<Vec<ReturnValue>, ShellError> {
|
|
Ok(vec![])
|
|
}
|
|
|
|
fn end_filter(&mut self) -> Result<Vec<ReturnValue>, ShellError> {
|
|
Ok(vec![])
|
|
}
|
|
|
|
fn sink(&mut self, _call_info: CallInfo, _input: Vec<Value>) {}
|
|
|
|
fn quit(&mut self) {}
|
|
}
|
|
|
|
pub fn serve_plugin(plugin: &mut dyn Plugin) {
|
|
let args = std::env::args();
|
|
if args.len() > 1 {
|
|
let input = args.skip(1).next();
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct JsonRpc<T> {
|
|
jsonrpc: String,
|
|
pub method: String,
|
|
pub params: T,
|
|
}
|
|
impl<T> JsonRpc<T> {
|
|
pub fn new<U: Into<String>>(method: U, params: T) -> Self {
|
|
JsonRpc {
|
|
jsonrpc: "2.0".into(),
|
|
method: method.into(),
|
|
params,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn send_response<T: Serialize>(result: T) {
|
|
let response = JsonRpc::new("response", result);
|
|
let response_raw = serde_json::to_string(&response);
|
|
|
|
match response_raw {
|
|
Ok(response) => outln!("{}", response),
|
|
Err(err) => outln!("{}", err),
|
|
}
|
|
}
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
#[serde(tag = "method")]
|
|
#[allow(non_camel_case_types)]
|
|
pub enum NuCommand {
|
|
config,
|
|
begin_filter { params: CallInfo },
|
|
filter { params: Value },
|
|
end_filter,
|
|
sink { params: (CallInfo, Vec<Value>) },
|
|
quit,
|
|
}
|