mirror of
https://github.com/nushell/nushell.git
synced 2025-04-01 11:46:20 +02:00
Merge pull request #288 from elferherrera/plugins
Multiple commands per plugin
This commit is contained in:
commit
8c43f60e2e
45
Cargo.lock
generated
45
Cargo.lock
generated
@ -395,6 +395,7 @@ dependencies = [
|
|||||||
"nu-json",
|
"nu-json",
|
||||||
"nu-parser",
|
"nu-parser",
|
||||||
"nu-path",
|
"nu-path",
|
||||||
|
"nu-plugin",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
"nu-table",
|
"nu-table",
|
||||||
"nu-term-grid",
|
"nu-term-grid",
|
||||||
@ -668,7 +669,6 @@ dependencies = [
|
|||||||
"nu-json",
|
"nu-json",
|
||||||
"nu-parser",
|
"nu-parser",
|
||||||
"nu-path",
|
"nu-path",
|
||||||
"nu-plugin",
|
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
"nu-table",
|
"nu-table",
|
||||||
"nu-term-grid",
|
"nu-term-grid",
|
||||||
@ -762,6 +762,16 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu_plugin_inc"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nu-engine",
|
||||||
|
"nu-plugin",
|
||||||
|
"nu-protocol",
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
@ -861,6 +871,15 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||||
|
dependencies = [
|
||||||
|
"ucd-trie",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@ -1125,6 +1144,24 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.130"
|
version = "1.0.130"
|
||||||
@ -1393,6 +1430,12 @@ version = "1.14.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
|
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ucd-trie"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uncased"
|
name = "uncased"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
|
@ -13,6 +13,7 @@ members = [
|
|||||||
"crates/nu-command",
|
"crates/nu-command",
|
||||||
"crates/nu-protocol",
|
"crates/nu-protocol",
|
||||||
"crates/nu-plugin",
|
"crates/nu-plugin",
|
||||||
|
"crates/nu_plugin_inc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@ -26,12 +27,17 @@ nu-json = { path="./crates/nu-json" }
|
|||||||
nu-parser = { path="./crates/nu-parser" }
|
nu-parser = { path="./crates/nu-parser" }
|
||||||
nu-path = { path="./crates/nu-path" }
|
nu-path = { path="./crates/nu-path" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol" }
|
nu-protocol = { path = "./crates/nu-protocol" }
|
||||||
|
nu-plugin = { path = "./crates/nu-plugin", optional = true }
|
||||||
nu-table = { path = "./crates/nu-table" }
|
nu-table = { path = "./crates/nu-table" }
|
||||||
nu-term-grid = { path = "./crates/nu-term-grid" }
|
nu-term-grid = { path = "./crates/nu-term-grid" }
|
||||||
miette = "3.0.0"
|
miette = "3.0.0"
|
||||||
ctrlc = "3.2.1"
|
ctrlc = "3.2.1"
|
||||||
# mimalloc = { version = "*", default-features = false }
|
# mimalloc = { version = "*", default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
plugin = ["nu-plugin", "nu-parser/plugin", "nu-command/plugin"]
|
||||||
|
default = ["plugin"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
assert_cmd = "1.0.7"
|
assert_cmd = "1.0.7"
|
||||||
|
@ -13,7 +13,6 @@ nu-protocol = { path = "../nu-protocol" }
|
|||||||
nu-table = { path = "../nu-table" }
|
nu-table = { path = "../nu-table" }
|
||||||
nu-term-grid = { path = "../nu-term-grid" }
|
nu-term-grid = { path = "../nu-term-grid" }
|
||||||
nu-parser = { path = "../nu-parser" }
|
nu-parser = { path = "../nu-parser" }
|
||||||
nu-plugin = { path = "../nu-plugin" }
|
|
||||||
|
|
||||||
trash = { version = "1.3.0", optional = true }
|
trash = { version = "1.3.0", optional = true }
|
||||||
unicode-segmentation = "1.8.0"
|
unicode-segmentation = "1.8.0"
|
||||||
@ -34,3 +33,4 @@ titlecase = "1.1.0"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
trash-support = ["trash"]
|
trash-support = ["trash"]
|
||||||
|
plugin = ["nu-parser/plugin"]
|
||||||
|
@ -9,8 +9,6 @@ mod hide;
|
|||||||
mod if_;
|
mod if_;
|
||||||
mod let_;
|
mod let_;
|
||||||
mod module;
|
mod module;
|
||||||
mod register;
|
|
||||||
mod run_plugin;
|
|
||||||
mod source;
|
mod source;
|
||||||
mod use_;
|
mod use_;
|
||||||
|
|
||||||
@ -25,7 +23,11 @@ pub use hide::Hide;
|
|||||||
pub use if_::If;
|
pub use if_::If;
|
||||||
pub use let_::Let;
|
pub use let_::Let;
|
||||||
pub use module::Module;
|
pub use module::Module;
|
||||||
pub use register::Register;
|
|
||||||
pub use run_plugin::RunPlugin;
|
|
||||||
pub use source::Source;
|
pub use source::Source;
|
||||||
pub use use_::Use;
|
pub use use_::Use;
|
||||||
|
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
mod register;
|
||||||
|
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
pub use register::Register;
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{PipelineData, Signature};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RunPlugin;
|
|
||||||
|
|
||||||
impl Command for RunPlugin {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"run_plugin"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"test for plugin encoding"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
|
||||||
Signature::build("run_plugin")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
_context: &EngineState,
|
|
||||||
_stack: &mut Stack,
|
|
||||||
_call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
|
||||||
Ok(PipelineData::new())
|
|
||||||
}
|
|
||||||
}
|
|
@ -79,10 +79,8 @@ pub fn create_default_context() -> EngineState {
|
|||||||
Mv,
|
Mv,
|
||||||
ParEach,
|
ParEach,
|
||||||
Ps,
|
Ps,
|
||||||
Register,
|
|
||||||
Range,
|
Range,
|
||||||
Rm,
|
Rm,
|
||||||
RunPlugin,
|
|
||||||
Select,
|
Select,
|
||||||
Size,
|
Size,
|
||||||
Split,
|
Split,
|
||||||
@ -101,6 +99,9 @@ pub fn create_default_context() -> EngineState {
|
|||||||
Zip
|
Zip
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
bind_command!(Register);
|
||||||
|
|
||||||
// This is a WIP proof of concept
|
// This is a WIP proof of concept
|
||||||
bind_command!(ListGitBranches, Git, GitCheckout, Source);
|
bind_command!(ListGitBranches, Git, GitCheckout, Source);
|
||||||
|
|
||||||
|
@ -7,4 +7,7 @@ edition = "2018"
|
|||||||
miette = "3.0.0"
|
miette = "3.0.0"
|
||||||
thiserror = "1.0.29"
|
thiserror = "1.0.29"
|
||||||
nu-protocol = { path = "../nu-protocol"}
|
nu-protocol = { path = "../nu-protocol"}
|
||||||
nu-plugin = { path = "../nu-plugin"}
|
nu-plugin = { path = "../nu-plugin", optional=true}
|
||||||
|
|
||||||
|
[features]
|
||||||
|
plugin = ["nu-plugin"]
|
||||||
|
@ -11,6 +11,9 @@ pub use flatten::{flatten_block, FlatShape};
|
|||||||
pub use lex::{lex, Token, TokenContents};
|
pub use lex::{lex, Token, TokenContents};
|
||||||
pub use lite_parse::{lite_parse, LiteBlock};
|
pub use lite_parse::{lite_parse, LiteBlock};
|
||||||
pub use parse_keywords::{
|
pub use parse_keywords::{
|
||||||
parse_alias, parse_def, parse_def_predecl, parse_let, parse_module, parse_plugin, parse_use,
|
parse_alias, parse_def, parse_def_predecl, parse_let, parse_module, parse_use,
|
||||||
};
|
};
|
||||||
pub use parser::{find_captures_in_expr, parse, Import, VarDecl};
|
pub use parser::{find_captures_in_expr, parse, Import, VarDecl};
|
||||||
|
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
pub use parse_keywords::parse_plugin;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use nu_plugin::plugin::{get_signature, PluginDeclaration};
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Block, Call, Expr, Expression, ImportPattern, ImportPatternMember, Pipeline, Statement},
|
ast::{Block, Call, Expr, Expression, ImportPattern, ImportPatternMember, Pipeline, Statement},
|
||||||
engine::StateWorkingSet,
|
engine::StateWorkingSet,
|
||||||
@ -6,6 +5,9 @@ use nu_protocol::{
|
|||||||
};
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
use nu_plugin::plugin::{get_signature, PluginDeclaration};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
lex, lite_parse,
|
lex, lite_parse,
|
||||||
parser::{
|
parser::{
|
||||||
@ -925,6 +927,7 @@ pub fn parse_source(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
pub fn parse_plugin(
|
pub fn parse_plugin(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
spans: &[Span],
|
spans: &[Span],
|
||||||
@ -960,11 +963,14 @@ pub fn parse_plugin(
|
|||||||
// get signature from plugin
|
// get signature from plugin
|
||||||
match get_signature(source_file) {
|
match get_signature(source_file) {
|
||||||
Err(err) => Some(ParseError::PluginError(format!("{}", err))),
|
Err(err) => Some(ParseError::PluginError(format!("{}", err))),
|
||||||
Ok(signature) => {
|
Ok(signatures) => {
|
||||||
// create plugin command declaration (need struct impl Command)
|
for signature in signatures {
|
||||||
// store declaration in working set
|
// create plugin command declaration (need struct impl Command)
|
||||||
let plugin_decl = PluginDeclaration::new(filename, signature);
|
// store declaration in working set
|
||||||
working_set.add_decl(Box::new(plugin_decl));
|
let plugin_decl =
|
||||||
|
PluginDeclaration::new(filename.clone(), signature);
|
||||||
|
working_set.add_decl(Box::new(plugin_decl));
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,12 @@ use nu_protocol::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::parse_keywords::{
|
use crate::parse_keywords::{
|
||||||
parse_alias, parse_def, parse_def_predecl, parse_hide, parse_let, parse_module, parse_plugin,
|
parse_alias, parse_def, parse_def_predecl, parse_hide, parse_let, parse_module, parse_use,
|
||||||
parse_use,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
use crate::parse_keywords::parse_plugin;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Import {}
|
pub enum Import {}
|
||||||
|
|
||||||
@ -3148,6 +3150,7 @@ pub fn parse_statement(
|
|||||||
Some(ParseError::UnexpectedKeyword("export".into(), spans[0])),
|
Some(ParseError::UnexpectedKeyword("export".into(), spans[0])),
|
||||||
),
|
),
|
||||||
b"hide" => parse_hide(working_set, spans),
|
b"hide" => parse_hide(working_set, spans),
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
b"register" => parse_plugin(working_set, spans),
|
b"register" => parse_plugin(working_set, spans),
|
||||||
_ => {
|
_ => {
|
||||||
let (expr, err) = parse_expression(working_set, spans, true);
|
let (expr, err) = parse_expression(working_set, spans, true);
|
||||||
|
@ -111,8 +111,9 @@ struct Call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct CallInfo {
|
struct CallInfo {
|
||||||
call @0: Call;
|
name @0: Text;
|
||||||
input @1: Value;
|
call @1: Call;
|
||||||
|
input @2: Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main communication structs with the plugin
|
# Main communication structs with the plugin
|
||||||
@ -126,7 +127,7 @@ struct PluginCall {
|
|||||||
struct PluginResponse {
|
struct PluginResponse {
|
||||||
union {
|
union {
|
||||||
error @0 :Text;
|
error @0 :Text;
|
||||||
signature @1 :Signature;
|
signature @1 :List(Signature);
|
||||||
value @2 :Value;
|
value @2 :Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ const OUTPUT_BUFFER_SIZE: usize = 8192;
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CallInfo {
|
pub struct CallInfo {
|
||||||
|
pub name: String,
|
||||||
pub call: Call,
|
pub call: Call,
|
||||||
pub input: Value,
|
pub input: Value,
|
||||||
}
|
}
|
||||||
@ -26,7 +27,7 @@ pub enum PluginCall {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PluginResponse {
|
pub enum PluginResponse {
|
||||||
Error(String),
|
Error(String),
|
||||||
Signature(Box<Signature>),
|
Signature(Vec<Signature>),
|
||||||
Value(Box<Value>),
|
Value(Box<Value>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ pub enum PluginError {
|
|||||||
UnableToSpawn(String),
|
UnableToSpawn(String),
|
||||||
EncodingError(String),
|
EncodingError(String),
|
||||||
DecodingError(String),
|
DecodingError(String),
|
||||||
|
RunTimeError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for PluginError {
|
impl Display for PluginError {
|
||||||
@ -53,11 +55,14 @@ impl Display for PluginError {
|
|||||||
PluginError::DecodingError(err) => {
|
PluginError::DecodingError(err) => {
|
||||||
write!(f, "error while decoding: {}", err)
|
write!(f, "error while decoding: {}", err)
|
||||||
}
|
}
|
||||||
|
PluginError::RunTimeError(err) => {
|
||||||
|
write!(f, "runtime error: {}", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_signature(path: &Path) -> Result<Box<Signature>, PluginError> {
|
pub fn get_signature(path: &Path) -> Result<Vec<Signature>, PluginError> {
|
||||||
let mut plugin_cmd = create_command(path);
|
let mut plugin_cmd = create_command(path);
|
||||||
|
|
||||||
// Both stdout and stdin are piped so we can get the information from the plugin
|
// Both stdout and stdin are piped so we can get the information from the plugin
|
||||||
@ -114,12 +119,12 @@ fn create_command(path: &Path) -> CommandSys {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PluginDeclaration {
|
pub struct PluginDeclaration {
|
||||||
name: String,
|
name: String,
|
||||||
signature: Box<Signature>,
|
signature: Signature,
|
||||||
filename: String,
|
filename: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PluginDeclaration {
|
impl PluginDeclaration {
|
||||||
pub fn new(filename: String, signature: Box<Signature>) -> Self {
|
pub fn new(filename: String, signature: Signature) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: signature.name.clone(),
|
name: signature.name.clone(),
|
||||||
signature,
|
signature,
|
||||||
@ -134,11 +139,11 @@ impl Command for PluginDeclaration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
self.signature.as_ref().clone()
|
self.signature.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"plugin name plus arguments"
|
self.signature.usage.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -175,6 +180,7 @@ impl Command for PluginDeclaration {
|
|||||||
|
|
||||||
// PluginCall information
|
// PluginCall information
|
||||||
let plugin_call = PluginCall::CallInfo(Box::new(CallInfo {
|
let plugin_call = PluginCall::CallInfo(Box::new(CallInfo {
|
||||||
|
name: self.name.clone(),
|
||||||
call: call.clone(),
|
call: call.clone(),
|
||||||
input,
|
input,
|
||||||
}));
|
}));
|
||||||
@ -221,8 +227,8 @@ impl Command for PluginDeclaration {
|
|||||||
|
|
||||||
/// The `Plugin` trait defines the API which plugins use to "hook" into nushell.
|
/// The `Plugin` trait defines the API which plugins use to "hook" into nushell.
|
||||||
pub trait Plugin {
|
pub trait Plugin {
|
||||||
fn signature(&self) -> Signature;
|
fn signature(&self) -> Vec<Signature>;
|
||||||
fn run(&self, call: &Call, input: &Value) -> Result<Value, PluginError>;
|
fn run(&mut self, name: &str, call: &Call, input: &Value) -> Result<Value, PluginError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function used in the plugin definition for the communication protocol between
|
// Function used in the plugin definition for the communication protocol between
|
||||||
@ -242,12 +248,12 @@ pub fn serve_plugin(plugin: &mut impl Plugin) {
|
|||||||
match plugin_call {
|
match plugin_call {
|
||||||
// Sending the signature back to nushell to create the declaration definition
|
// Sending the signature back to nushell to create the declaration definition
|
||||||
PluginCall::Signature => {
|
PluginCall::Signature => {
|
||||||
let response = PluginResponse::Signature(Box::new(plugin.signature()));
|
let response = PluginResponse::Signature(plugin.signature());
|
||||||
encode_response(&response, &mut std::io::stdout())
|
encode_response(&response, &mut std::io::stdout())
|
||||||
.expect("Error encoding response");
|
.expect("Error encoding response");
|
||||||
}
|
}
|
||||||
PluginCall::CallInfo(call_info) => {
|
PluginCall::CallInfo(call_info) => {
|
||||||
let value = plugin.run(&call_info.call, &call_info.input);
|
let value = plugin.run(&call_info.name, &call_info.call, &call_info.input);
|
||||||
|
|
||||||
let response = match value {
|
let response = match value {
|
||||||
Ok(value) => PluginResponse::Value(Box::new(value)),
|
Ok(value) => PluginResponse::Value(Box::new(value)),
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use crate::plugin::{CallInfo, PluginCall, PluginError, PluginResponse};
|
use crate::plugin::{CallInfo, PluginCall, PluginError, PluginResponse};
|
||||||
use crate::plugin_capnp::{plugin_call, plugin_response};
|
use crate::plugin_capnp::{plugin_call, plugin_response};
|
||||||
|
use crate::serializers::signature::deserialize_signature;
|
||||||
use crate::serializers::{call, signature, value};
|
use crate::serializers::{call, signature, value};
|
||||||
use capnp::serialize_packed;
|
use capnp::serialize_packed;
|
||||||
|
use nu_protocol::Signature;
|
||||||
|
|
||||||
pub fn encode_call(
|
pub fn encode_call(
|
||||||
plugin_call: &PluginCall,
|
plugin_call: &PluginCall,
|
||||||
@ -16,6 +18,9 @@ pub fn encode_call(
|
|||||||
PluginCall::CallInfo(call_info) => {
|
PluginCall::CallInfo(call_info) => {
|
||||||
let mut call_info_builder = builder.reborrow().init_call_info();
|
let mut call_info_builder = builder.reborrow().init_call_info();
|
||||||
|
|
||||||
|
// Serializing name from the call
|
||||||
|
call_info_builder.set_name(call_info.name.as_str());
|
||||||
|
|
||||||
// Serializing argument information from the call
|
// Serializing argument information from the call
|
||||||
let call_builder = call_info_builder
|
let call_builder = call_info_builder
|
||||||
.reborrow()
|
.reborrow()
|
||||||
@ -53,6 +58,10 @@ pub fn decode_call(reader: &mut impl std::io::BufRead) -> Result<PluginCall, Plu
|
|||||||
Ok(plugin_call::CallInfo(reader)) => {
|
Ok(plugin_call::CallInfo(reader)) => {
|
||||||
let reader = reader.map_err(|e| PluginError::DecodingError(e.to_string()))?;
|
let reader = reader.map_err(|e| PluginError::DecodingError(e.to_string()))?;
|
||||||
|
|
||||||
|
let name = reader
|
||||||
|
.get_name()
|
||||||
|
.map_err(|e| PluginError::DecodingError(e.to_string()))?;
|
||||||
|
|
||||||
let call_reader = reader
|
let call_reader = reader
|
||||||
.get_call()
|
.get_call()
|
||||||
.map_err(|e| PluginError::DecodingError(e.to_string()))?;
|
.map_err(|e| PluginError::DecodingError(e.to_string()))?;
|
||||||
@ -67,7 +76,11 @@ pub fn decode_call(reader: &mut impl std::io::BufRead) -> Result<PluginCall, Plu
|
|||||||
let input = value::deserialize_value(input_reader)
|
let input = value::deserialize_value(input_reader)
|
||||||
.map_err(|e| PluginError::DecodingError(e.to_string()))?;
|
.map_err(|e| PluginError::DecodingError(e.to_string()))?;
|
||||||
|
|
||||||
Ok(PluginCall::CallInfo(Box::new(CallInfo { call, input })))
|
Ok(PluginCall::CallInfo(Box::new(CallInfo {
|
||||||
|
name: name.to_string(),
|
||||||
|
call,
|
||||||
|
input,
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,9 +95,14 @@ pub fn encode_response(
|
|||||||
|
|
||||||
match &plugin_response {
|
match &plugin_response {
|
||||||
PluginResponse::Error(msg) => builder.reborrow().set_error(msg.as_str()),
|
PluginResponse::Error(msg) => builder.reborrow().set_error(msg.as_str()),
|
||||||
PluginResponse::Signature(sign) => {
|
PluginResponse::Signature(signatures) => {
|
||||||
let signature_builder = builder.reborrow().init_signature();
|
let mut signature_list_builder =
|
||||||
signature::serialize_signature(sign, signature_builder)
|
builder.reborrow().init_signature(signatures.len() as u32);
|
||||||
|
|
||||||
|
for (index, signature) in signatures.iter().enumerate() {
|
||||||
|
let signature_builder = signature_list_builder.reborrow().get(index as u32);
|
||||||
|
signature::serialize_signature(signature, signature_builder)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PluginResponse::Value(val) => {
|
PluginResponse::Value(val) => {
|
||||||
let value_builder = builder.reborrow().init_value();
|
let value_builder = builder.reborrow().init_value();
|
||||||
@ -113,10 +131,13 @@ pub fn decode_response(reader: &mut impl std::io::BufRead) -> Result<PluginRespo
|
|||||||
}
|
}
|
||||||
Ok(plugin_response::Signature(reader)) => {
|
Ok(plugin_response::Signature(reader)) => {
|
||||||
let reader = reader.map_err(|e| PluginError::DecodingError(e.to_string()))?;
|
let reader = reader.map_err(|e| PluginError::DecodingError(e.to_string()))?;
|
||||||
let sign = signature::deserialize_signature(reader)
|
|
||||||
.map_err(|e| PluginError::DecodingError(e.to_string()))?;
|
|
||||||
|
|
||||||
Ok(PluginResponse::Signature(Box::new(sign)))
|
let signatures = reader
|
||||||
|
.iter()
|
||||||
|
.map(deserialize_signature)
|
||||||
|
.collect::<Result<Vec<Signature>, PluginError>>()?;
|
||||||
|
|
||||||
|
Ok(PluginResponse::Signature(signatures))
|
||||||
}
|
}
|
||||||
Ok(plugin_response::Value(reader)) => {
|
Ok(plugin_response::Value(reader)) => {
|
||||||
let reader = reader.map_err(|e| PluginError::DecodingError(e.to_string()))?;
|
let reader = reader.map_err(|e| PluginError::DecodingError(e.to_string()))?;
|
||||||
@ -163,6 +184,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn callinfo_round_trip_callinfo() {
|
fn callinfo_round_trip_callinfo() {
|
||||||
|
let name = "test".to_string();
|
||||||
|
|
||||||
let input = Value::Bool {
|
let input = Value::Bool {
|
||||||
val: false,
|
val: false,
|
||||||
span: Span { start: 1, end: 20 },
|
span: Span { start: 1, end: 20 },
|
||||||
@ -200,6 +223,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let plugin_call = PluginCall::CallInfo(Box::new(CallInfo {
|
let plugin_call = PluginCall::CallInfo(Box::new(CallInfo {
|
||||||
|
name: name.clone(),
|
||||||
call: call.clone(),
|
call: call.clone(),
|
||||||
input: input.clone(),
|
input: input.clone(),
|
||||||
}));
|
}));
|
||||||
@ -211,6 +235,7 @@ mod tests {
|
|||||||
match returned {
|
match returned {
|
||||||
PluginCall::Signature => panic!("returned wrong call type"),
|
PluginCall::Signature => panic!("returned wrong call type"),
|
||||||
PluginCall::CallInfo(call_info) => {
|
PluginCall::CallInfo(call_info) => {
|
||||||
|
assert_eq!(name, call_info.name);
|
||||||
assert_eq!(input, call_info.input);
|
assert_eq!(input, call_info.input);
|
||||||
assert_eq!(call.head, call_info.call.head);
|
assert_eq!(call.head, call_info.call.head);
|
||||||
assert_eq!(call.positional.len(), call_info.call.positional.len());
|
assert_eq!(call.positional.len(), call_info.call.positional.len());
|
||||||
@ -251,7 +276,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.rest("remaining", SyntaxShape::Int, "remaining");
|
.rest("remaining", SyntaxShape::Int, "remaining");
|
||||||
|
|
||||||
let response = PluginResponse::Signature(Box::new(signature.clone()));
|
let response = PluginResponse::Signature(vec![signature.clone()]);
|
||||||
|
|
||||||
let mut buffer: Vec<u8> = Vec::new();
|
let mut buffer: Vec<u8> = Vec::new();
|
||||||
encode_response(&response, &mut buffer).expect("unable to serialize message");
|
encode_response(&response, &mut buffer).expect("unable to serialize message");
|
||||||
@ -262,32 +287,33 @@ mod tests {
|
|||||||
PluginResponse::Error(_) => panic!("returned wrong call type"),
|
PluginResponse::Error(_) => panic!("returned wrong call type"),
|
||||||
PluginResponse::Value(_) => panic!("returned wrong call type"),
|
PluginResponse::Value(_) => panic!("returned wrong call type"),
|
||||||
PluginResponse::Signature(returned_signature) => {
|
PluginResponse::Signature(returned_signature) => {
|
||||||
assert_eq!(signature.name, returned_signature.name);
|
assert!(returned_signature.len() == 1);
|
||||||
assert_eq!(signature.usage, returned_signature.usage);
|
assert_eq!(signature.name, returned_signature[0].name);
|
||||||
assert_eq!(signature.extra_usage, returned_signature.extra_usage);
|
assert_eq!(signature.usage, returned_signature[0].usage);
|
||||||
assert_eq!(signature.is_filter, returned_signature.is_filter);
|
assert_eq!(signature.extra_usage, returned_signature[0].extra_usage);
|
||||||
|
assert_eq!(signature.is_filter, returned_signature[0].is_filter);
|
||||||
|
|
||||||
signature
|
signature
|
||||||
.required_positional
|
.required_positional
|
||||||
.iter()
|
.iter()
|
||||||
.zip(returned_signature.required_positional.iter())
|
.zip(returned_signature[0].required_positional.iter())
|
||||||
.for_each(|(lhs, rhs)| assert_eq!(lhs, rhs));
|
.for_each(|(lhs, rhs)| assert_eq!(lhs, rhs));
|
||||||
|
|
||||||
signature
|
signature
|
||||||
.optional_positional
|
.optional_positional
|
||||||
.iter()
|
.iter()
|
||||||
.zip(returned_signature.optional_positional.iter())
|
.zip(returned_signature[0].optional_positional.iter())
|
||||||
.for_each(|(lhs, rhs)| assert_eq!(lhs, rhs));
|
.for_each(|(lhs, rhs)| assert_eq!(lhs, rhs));
|
||||||
|
|
||||||
signature
|
signature
|
||||||
.named
|
.named
|
||||||
.iter()
|
.iter()
|
||||||
.zip(returned_signature.named.iter())
|
.zip(returned_signature[0].named.iter())
|
||||||
.for_each(|(lhs, rhs)| assert_eq!(lhs, rhs));
|
.for_each(|(lhs, rhs)| assert_eq!(lhs, rhs));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
signature.rest_positional,
|
signature.rest_positional,
|
||||||
returned_signature.rest_positional,
|
returned_signature[0].rest_positional,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3480,25 +3480,35 @@ pub mod call_info {
|
|||||||
self.reader.total_size()
|
self.reader.total_size()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_call(self) -> ::capnp::Result<crate::plugin_capnp::call::Reader<'a>> {
|
pub fn get_name(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(
|
::capnp::traits::FromPointerReader::get_from_pointer(
|
||||||
&self.reader.get_pointer_field(0),
|
&self.reader.get_pointer_field(0),
|
||||||
::core::option::Option::None,
|
::core::option::Option::None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub fn has_call(&self) -> bool {
|
pub fn has_name(&self) -> bool {
|
||||||
!self.reader.get_pointer_field(0).is_null()
|
!self.reader.get_pointer_field(0).is_null()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_input(self) -> ::capnp::Result<crate::plugin_capnp::value::Reader<'a>> {
|
pub fn get_call(self) -> ::capnp::Result<crate::plugin_capnp::call::Reader<'a>> {
|
||||||
::capnp::traits::FromPointerReader::get_from_pointer(
|
::capnp::traits::FromPointerReader::get_from_pointer(
|
||||||
&self.reader.get_pointer_field(1),
|
&self.reader.get_pointer_field(1),
|
||||||
::core::option::Option::None,
|
::core::option::Option::None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub fn has_input(&self) -> bool {
|
pub fn has_call(&self) -> bool {
|
||||||
!self.reader.get_pointer_field(1).is_null()
|
!self.reader.get_pointer_field(1).is_null()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get_input(self) -> ::capnp::Result<crate::plugin_capnp::value::Reader<'a>> {
|
||||||
|
::capnp::traits::FromPointerReader::get_from_pointer(
|
||||||
|
&self.reader.get_pointer_field(2),
|
||||||
|
::core::option::Option::None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn has_input(&self) -> bool {
|
||||||
|
!self.reader.get_pointer_field(2).is_null()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Builder<'a> {
|
pub struct Builder<'a> {
|
||||||
@ -3571,34 +3581,52 @@ pub mod call_info {
|
|||||||
self.builder.into_reader().total_size()
|
self.builder.into_reader().total_size()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_call(self) -> ::capnp::Result<crate::plugin_capnp::call::Builder<'a>> {
|
pub fn get_name(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(
|
::capnp::traits::FromPointerBuilder::get_from_pointer(
|
||||||
self.builder.get_pointer_field(0),
|
self.builder.get_pointer_field(0),
|
||||||
::core::option::Option::None,
|
::core::option::Option::None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
|
pub fn set_name(&mut self, value: ::capnp::text::Reader<'_>) {
|
||||||
|
self.builder.get_pointer_field(0).set_text(value);
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn init_name(self, size: u32) -> ::capnp::text::Builder<'a> {
|
||||||
|
self.builder.get_pointer_field(0).init_text(size)
|
||||||
|
}
|
||||||
|
pub fn has_name(&self) -> bool {
|
||||||
|
!self.builder.get_pointer_field(0).is_null()
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get_call(self) -> ::capnp::Result<crate::plugin_capnp::call::Builder<'a>> {
|
||||||
|
::capnp::traits::FromPointerBuilder::get_from_pointer(
|
||||||
|
self.builder.get_pointer_field(1),
|
||||||
|
::core::option::Option::None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
pub fn set_call(
|
pub fn set_call(
|
||||||
&mut self,
|
&mut self,
|
||||||
value: crate::plugin_capnp::call::Reader<'_>,
|
value: crate::plugin_capnp::call::Reader<'_>,
|
||||||
) -> ::capnp::Result<()> {
|
) -> ::capnp::Result<()> {
|
||||||
::capnp::traits::SetPointerBuilder::set_pointer_builder(
|
::capnp::traits::SetPointerBuilder::set_pointer_builder(
|
||||||
self.builder.get_pointer_field(0),
|
self.builder.get_pointer_field(1),
|
||||||
value,
|
value,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn init_call(self) -> crate::plugin_capnp::call::Builder<'a> {
|
pub fn init_call(self) -> crate::plugin_capnp::call::Builder<'a> {
|
||||||
::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0)
|
::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(1), 0)
|
||||||
}
|
}
|
||||||
pub fn has_call(&self) -> bool {
|
pub fn has_call(&self) -> bool {
|
||||||
!self.builder.get_pointer_field(0).is_null()
|
!self.builder.get_pointer_field(1).is_null()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_input(self) -> ::capnp::Result<crate::plugin_capnp::value::Builder<'a>> {
|
pub fn get_input(self) -> ::capnp::Result<crate::plugin_capnp::value::Builder<'a>> {
|
||||||
::capnp::traits::FromPointerBuilder::get_from_pointer(
|
::capnp::traits::FromPointerBuilder::get_from_pointer(
|
||||||
self.builder.get_pointer_field(1),
|
self.builder.get_pointer_field(2),
|
||||||
::core::option::Option::None,
|
::core::option::Option::None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -3608,17 +3636,17 @@ pub mod call_info {
|
|||||||
value: crate::plugin_capnp::value::Reader<'_>,
|
value: crate::plugin_capnp::value::Reader<'_>,
|
||||||
) -> ::capnp::Result<()> {
|
) -> ::capnp::Result<()> {
|
||||||
::capnp::traits::SetPointerBuilder::set_pointer_builder(
|
::capnp::traits::SetPointerBuilder::set_pointer_builder(
|
||||||
self.builder.get_pointer_field(1),
|
self.builder.get_pointer_field(2),
|
||||||
value,
|
value,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn init_input(self) -> crate::plugin_capnp::value::Builder<'a> {
|
pub fn init_input(self) -> crate::plugin_capnp::value::Builder<'a> {
|
||||||
::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(1), 0)
|
::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(2), 0)
|
||||||
}
|
}
|
||||||
pub fn has_input(&self) -> bool {
|
pub fn has_input(&self) -> bool {
|
||||||
!self.builder.get_pointer_field(1).is_null()
|
!self.builder.get_pointer_field(2).is_null()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3634,17 +3662,17 @@ pub mod call_info {
|
|||||||
}
|
}
|
||||||
impl Pipeline {
|
impl Pipeline {
|
||||||
pub fn get_call(&self) -> crate::plugin_capnp::call::Pipeline {
|
pub fn get_call(&self) -> crate::plugin_capnp::call::Pipeline {
|
||||||
::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(0))
|
::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(1))
|
||||||
}
|
}
|
||||||
pub fn get_input(&self) -> crate::plugin_capnp::value::Pipeline {
|
pub fn get_input(&self) -> crate::plugin_capnp::value::Pipeline {
|
||||||
::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(1))
|
::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mod _private {
|
mod _private {
|
||||||
use capnp::private::layout;
|
use capnp::private::layout;
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize {
|
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize {
|
||||||
data: 0,
|
data: 0,
|
||||||
pointers: 2,
|
pointers: 3,
|
||||||
};
|
};
|
||||||
pub const TYPE_ID: u64 = 0x8e03_127e_9170_7d6a;
|
pub const TYPE_ID: u64 = 0x8e03_127e_9170_7d6a;
|
||||||
}
|
}
|
||||||
@ -4073,7 +4101,7 @@ pub mod plugin_response {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_signature(
|
pub fn set_signature(
|
||||||
&mut self,
|
&mut self,
|
||||||
value: crate::plugin_capnp::signature::Reader<'_>,
|
value: ::capnp::struct_list::Reader<'a, crate::plugin_capnp::signature::Owned>,
|
||||||
) -> ::capnp::Result<()> {
|
) -> ::capnp::Result<()> {
|
||||||
self.builder.set_data_field::<u16>(0, 1);
|
self.builder.set_data_field::<u16>(0, 1);
|
||||||
::capnp::traits::SetPointerBuilder::set_pointer_builder(
|
::capnp::traits::SetPointerBuilder::set_pointer_builder(
|
||||||
@ -4083,9 +4111,15 @@ pub mod plugin_response {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn init_signature(self) -> crate::plugin_capnp::signature::Builder<'a> {
|
pub fn init_signature(
|
||||||
|
self,
|
||||||
|
size: u32,
|
||||||
|
) -> ::capnp::struct_list::Builder<'a, crate::plugin_capnp::signature::Owned> {
|
||||||
self.builder.set_data_field::<u16>(0, 1);
|
self.builder.set_data_field::<u16>(0, 1);
|
||||||
::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0)
|
::capnp::traits::FromPointerBuilder::init_pointer(
|
||||||
|
self.builder.get_pointer_field(0),
|
||||||
|
size,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
pub fn has_signature(&self) -> bool {
|
pub fn has_signature(&self) -> bool {
|
||||||
if self.builder.get_data_field::<u16>(0) != 1 {
|
if self.builder.get_data_field::<u16>(0) != 1 {
|
||||||
@ -4168,12 +4202,12 @@ pub mod plugin_response {
|
|||||||
}
|
}
|
||||||
pub type WhichReader<'a> = Which<
|
pub type WhichReader<'a> = Which<
|
||||||
::capnp::Result<::capnp::text::Reader<'a>>,
|
::capnp::Result<::capnp::text::Reader<'a>>,
|
||||||
::capnp::Result<crate::plugin_capnp::signature::Reader<'a>>,
|
::capnp::Result<::capnp::struct_list::Reader<'a, crate::plugin_capnp::signature::Owned>>,
|
||||||
::capnp::Result<crate::plugin_capnp::value::Reader<'a>>,
|
::capnp::Result<crate::plugin_capnp::value::Reader<'a>>,
|
||||||
>;
|
>;
|
||||||
pub type WhichBuilder<'a> = Which<
|
pub type WhichBuilder<'a> = Which<
|
||||||
::capnp::Result<::capnp::text::Builder<'a>>,
|
::capnp::Result<::capnp::text::Builder<'a>>,
|
||||||
::capnp::Result<crate::plugin_capnp::signature::Builder<'a>>,
|
::capnp::Result<::capnp::struct_list::Builder<'a, crate::plugin_capnp::signature::Owned>>,
|
||||||
::capnp::Result<crate::plugin_capnp::value::Builder<'a>>,
|
::capnp::Result<crate::plugin_capnp::value::Builder<'a>>,
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
17
crates/nu_plugin_inc/Cargo.toml
Normal file
17
crates/nu_plugin_inc/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["The Nu Project Contributors"]
|
||||||
|
description = "A version incrementer plugin for Nushell"
|
||||||
|
edition = "2018"
|
||||||
|
license = "MIT"
|
||||||
|
name = "nu_plugin_inc"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nu-plugin = { path="../nu-plugin", version = "0.1.0" }
|
||||||
|
nu-protocol = { path="../nu-protocol", version = "0.1.0" }
|
||||||
|
nu-engine = { path="../nu-engine", version = "0.1.0" }
|
||||||
|
|
||||||
|
semver = "0.11.0"
|
138
crates/nu_plugin_inc/src/inc.rs
Normal file
138
crates/nu_plugin_inc/src/inc.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use nu_plugin::plugin::PluginError;
|
||||||
|
use nu_protocol::{Span, Value};
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub enum Action {
|
||||||
|
SemVerAction(SemVerAction),
|
||||||
|
Default,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub enum SemVerAction {
|
||||||
|
Major,
|
||||||
|
Minor,
|
||||||
|
Patch,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Inc {
|
||||||
|
pub error: Option<String>,
|
||||||
|
pub action: Option<Action>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inc {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply(&self, input: &str) -> Value {
|
||||||
|
match &self.action {
|
||||||
|
Some(Action::SemVerAction(act_on)) => {
|
||||||
|
let mut ver = match semver::Version::parse(input) {
|
||||||
|
Ok(parsed_ver) => parsed_ver,
|
||||||
|
Err(_) => {
|
||||||
|
return Value::String {
|
||||||
|
val: input.to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match act_on {
|
||||||
|
SemVerAction::Major => ver.increment_major(),
|
||||||
|
SemVerAction::Minor => ver.increment_minor(),
|
||||||
|
SemVerAction::Patch => ver.increment_patch(),
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::String {
|
||||||
|
val: ver.to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Action::Default) | None => match input.parse::<u64>() {
|
||||||
|
Ok(v) => Value::String {
|
||||||
|
val: (v + 1).to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
Err(_) => Value::String {
|
||||||
|
val: input.to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn for_semver(&mut self, part: SemVerAction) {
|
||||||
|
if self.permit() {
|
||||||
|
self.action = Some(Action::SemVerAction(part));
|
||||||
|
} else {
|
||||||
|
self.log_error("can only apply one");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn permit(&mut self) -> bool {
|
||||||
|
self.action.is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_error(&mut self, message: &str) {
|
||||||
|
self.error = Some(message.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn usage() -> &'static str {
|
||||||
|
"Usage: inc field [--major|--minor|--patch]"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inc(&self, value: &Value) -> Result<Value, PluginError> {
|
||||||
|
match value {
|
||||||
|
Value::Int { val, span } => Ok(Value::Int {
|
||||||
|
val: val + 1,
|
||||||
|
span: span.clone(),
|
||||||
|
}),
|
||||||
|
Value::String { val, .. } => Ok(self.apply(val)),
|
||||||
|
_ => Err(PluginError::RunTimeError("incrementable value".to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
mod semver {
|
||||||
|
use nu_protocol::{Span, Value};
|
||||||
|
|
||||||
|
use crate::inc::SemVerAction;
|
||||||
|
use crate::Inc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn major() {
|
||||||
|
let expected = Value::String {
|
||||||
|
val: "1.0.0".to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
};
|
||||||
|
let mut inc = Inc::new();
|
||||||
|
inc.for_semver(SemVerAction::Major);
|
||||||
|
assert_eq!(inc.apply("0.1.3"), expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn minor() {
|
||||||
|
let expected = Value::String {
|
||||||
|
val: "0.2.0".to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
};
|
||||||
|
let mut inc = Inc::new();
|
||||||
|
inc.for_semver(SemVerAction::Minor);
|
||||||
|
assert_eq!(inc.apply("0.1.3"), expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn patch() {
|
||||||
|
let expected = Value::String {
|
||||||
|
val: "0.1.4".to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
};
|
||||||
|
let mut inc = Inc::new();
|
||||||
|
inc.for_semver(SemVerAction::Patch);
|
||||||
|
assert_eq!(inc.apply("0.1.3"), expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
crates/nu_plugin_inc/src/lib.rs
Normal file
4
crates/nu_plugin_inc/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
mod inc;
|
||||||
|
mod nu;
|
||||||
|
|
||||||
|
pub use inc::Inc;
|
6
crates/nu_plugin_inc/src/main.rs
Normal file
6
crates/nu_plugin_inc/src/main.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
use nu_plugin::serve_plugin;
|
||||||
|
use nu_plugin_inc::Inc;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
serve_plugin(&mut Inc::new())
|
||||||
|
}
|
47
crates/nu_plugin_inc/src/nu/mod.rs
Normal file
47
crates/nu_plugin_inc/src/nu/mod.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use crate::inc::SemVerAction;
|
||||||
|
use crate::Inc;
|
||||||
|
use nu_plugin::{plugin::PluginError, Plugin};
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::{Signature, Span, Value};
|
||||||
|
|
||||||
|
impl Plugin for Inc {
|
||||||
|
fn signature(&self) -> Vec<Signature> {
|
||||||
|
vec![Signature::build("inc")
|
||||||
|
.desc("Increment a value or version. Optionally use the column of a table.")
|
||||||
|
.switch(
|
||||||
|
"major",
|
||||||
|
"increment the major version (eg 1.2.1 -> 2.0.0)",
|
||||||
|
Some('M'),
|
||||||
|
)
|
||||||
|
.switch(
|
||||||
|
"minor",
|
||||||
|
"increment the minor version (eg 1.2.1 -> 1.3.0)",
|
||||||
|
Some('m'),
|
||||||
|
)
|
||||||
|
.switch(
|
||||||
|
"patch",
|
||||||
|
"increment the patch version (eg 1.2.1 -> 1.2.2)",
|
||||||
|
Some('p'),
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self, name: &str, call: &Call, input: &Value) -> Result<Value, PluginError> {
|
||||||
|
if name != "inc" {
|
||||||
|
return Ok(Value::Nothing {
|
||||||
|
span: Span::unknown(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if call.has_flag("major") {
|
||||||
|
self.for_semver(SemVerAction::Major);
|
||||||
|
}
|
||||||
|
if call.has_flag("minor") {
|
||||||
|
self.for_semver(SemVerAction::Minor);
|
||||||
|
}
|
||||||
|
if call.has_flag("patch") {
|
||||||
|
self.for_semver(SemVerAction::Patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inc(input)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user