From 01d30a416b0edb1c022489d37a1448c042ce9920 Mon Sep 17 00:00:00 2001 From: Devyn Cairns Date: Wed, 27 Mar 2024 03:59:57 -0700 Subject: [PATCH] Change PluginCommand API to be more like Command (#12279) # Description This is something that was discussed in the core team meeting last Wednesday. @ayax79 is building `nu-plugin-polars` with all of the dataframe commands into a plugin, and there are a lot of them, so it would help to make the API more similar. At the same time, I think the `Command` API is just better anyway. I don't think the difference is justified, and the types for core commands have the benefit of requiring less `.into()` because they often don't own their data - Broke `signature()` up into `name()`, `usage()`, `extra_usage()`, `search_terms()`, `examples()` - `signature()` returns `nu_protocol::Signature` - `examples()` returns `Vec` - `PluginSignature` and `PluginExample` no longer need to be used by plugin developers # User-Facing Changes Breaking API for plugins yet again :smile: --- .../src/fake_register.rs | 6 +- crates/nu-plugin-test-support/src/lib.rs | 64 ++++-- .../nu-plugin-test-support/src/plugin_test.rs | 16 +- .../tests/custom_value/mod.rs | 42 ++-- .../nu-plugin-test-support/tests/hello/mod.rs | 40 ++-- .../tests/lowercase/mod.rs | 43 ++-- crates/nu-plugin/src/lib.rs | 21 +- crates/nu-plugin/src/plugin/command.rs | 194 +++++++++++++++-- .../src/plugin/interface/engine/tests.rs | 11 +- crates/nu-plugin/src/plugin/mod.rs | 66 +++--- crates/nu-plugin/src/serializers/tests.rs | 29 ++- crates/nu-protocol/src/engine/engine_state.rs | 6 +- crates/nu-protocol/src/example.rs | 10 + crates/nu-protocol/src/plugin/signature.rs | 206 +----------------- .../nu_plugin_custom_values/src/drop_check.rs | 16 +- .../nu_plugin_custom_values/src/generate.rs | 29 ++- .../nu_plugin_custom_values/src/generate2.rs | 44 ++-- crates/nu_plugin_custom_values/src/update.rs | 45 ++-- .../nu_plugin_custom_values/src/update_arg.rs | 15 +- .../src/commands/collect_external.rs | 41 ++-- .../nu_plugin_example/src/commands/config.rs | 31 ++- .../src/commands/disable_gc.rs | 40 ++-- crates/nu_plugin_example/src/commands/env.rs | 31 ++- .../src/commands/for_each.rs | 43 ++-- .../src/commands/generate.rs | 54 +++-- crates/nu_plugin_example/src/commands/main.rs | 34 ++- crates/nu_plugin_example/src/commands/one.rs | 46 ++-- crates/nu_plugin_example/src/commands/seq.rs | 50 +++-- crates/nu_plugin_example/src/commands/sum.rs | 43 ++-- .../nu_plugin_example/src/commands/three.rs | 21 +- crates/nu_plugin_example/src/commands/two.rs | 21 +- crates/nu_plugin_example/src/example.rs | 4 +- crates/nu_plugin_example/src/lib.rs | 4 +- crates/nu_plugin_example/src/main.rs | 4 +- crates/nu_plugin_formats/src/from/eml.rs | 38 ++-- crates/nu_plugin_formats/src/from/ics.rs | 35 +-- crates/nu_plugin_formats/src/from/ini.rs | 32 ++- crates/nu_plugin_formats/src/from/vcf.rs | 32 ++- crates/nu_plugin_gstat/src/nu/mod.rs | 15 +- crates/nu_plugin_inc/src/nu/mod.rs | 15 +- crates/nu_plugin_query/src/query.rs | 16 +- crates/nu_plugin_query/src/query_json.rs | 19 +- crates/nu_plugin_query/src/query_web.rs | 47 ++-- crates/nu_plugin_query/src/query_xml.rs | 15 +- tests/plugins/register.rs | 2 +- 45 files changed, 962 insertions(+), 674 deletions(-) diff --git a/crates/nu-plugin-test-support/src/fake_register.rs b/crates/nu-plugin-test-support/src/fake_register.rs index fa05f7fbf4..e181de3899 100644 --- a/crates/nu-plugin-test-support/src/fake_register.rs +++ b/crates/nu-plugin-test-support/src/fake_register.rs @@ -1,6 +1,6 @@ -use std::sync::Arc; +use std::{ops::Deref, sync::Arc}; -use nu_plugin::{Plugin, PluginDeclaration}; +use nu_plugin::{create_plugin_signature, Plugin, PluginDeclaration}; use nu_protocol::{engine::StateWorkingSet, RegisteredPlugin, ShellError}; use crate::{fake_persistent_plugin::FakePersistentPlugin, spawn_fake_plugin::spawn_fake_plugin}; @@ -15,7 +15,7 @@ pub fn fake_register( let reg_plugin_clone = reg_plugin.clone(); for command in plugin.commands() { - let signature = command.signature(); + let signature = create_plugin_signature(command.deref()); let decl = PluginDeclaration::new(reg_plugin.clone(), signature); working_set.add_decl(Box::new(decl)); } diff --git a/crates/nu-plugin-test-support/src/lib.rs b/crates/nu-plugin-test-support/src/lib.rs index 8097e7460f..dd301d4d2b 100644 --- a/crates/nu-plugin-test-support/src/lib.rs +++ b/crates/nu-plugin-test-support/src/lib.rs @@ -7,8 +7,10 @@ //! //! use nu_plugin::*; //! use nu_plugin_test_support::PluginTest; -//! use nu_protocol::{PluginSignature, PipelineData, Type, Span, Value, LabeledError}; -//! use nu_protocol::IntoInterruptiblePipelineData; +//! use nu_protocol::{ +//! Example, IntoInterruptiblePipelineData, LabeledError, PipelineData, ShellError, Signature, +//! Span, Type, Value, +//! }; //! //! struct LowercasePlugin; //! struct Lowercase; @@ -16,26 +18,50 @@ //! impl PluginCommand for Lowercase { //! type Plugin = LowercasePlugin; //! -//! fn signature(&self) -> PluginSignature { -//! PluginSignature::build("lowercase") -//! .usage("Convert each string in a stream to lowercase") -//! .input_output_type(Type::List(Type::String.into()), Type::List(Type::String.into())) +//! fn name(&self) -> &str { +//! "lowercase" +//! } +//! +//! fn usage(&self) -> &str { +//! "Convert each string in a stream to lowercase" +//! } +//! +//! fn signature(&self) -> Signature { +//! Signature::build(self.name()).input_output_type( +//! Type::List(Type::String.into()), +//! Type::List(Type::String.into()), +//! ) +//! } +//! +//! fn examples(&self) -> Vec { +//! vec![Example { +//! example: r#"[Hello wORLD] | lowercase"#, +//! description: "Lowercase a list of strings", +//! result: Some(Value::test_list(vec![ +//! Value::test_string("hello"), +//! Value::test_string("world"), +//! ])), +//! }] //! } //! //! fn run( //! &self, -//! plugin: &LowercasePlugin, -//! engine: &EngineInterface, +//! _plugin: &LowercasePlugin, +//! _engine: &EngineInterface, //! call: &EvaluatedCall, //! input: PipelineData, //! ) -> Result { //! let span = call.head; -//! Ok(input.map(move |value| { -//! value.as_str() -//! .map(|string| Value::string(string.to_lowercase(), span)) -//! // Errors in a stream should be returned as values. -//! .unwrap_or_else(|err| Value::error(err, span)) -//! }, None)?) +//! Ok(input.map( +//! move |value| { +//! value +//! .as_str() +//! .map(|string| Value::string(string.to_lowercase(), span)) +//! // Errors in a stream should be returned as values. +//! .unwrap_or_else(|err| Value::error(err, span)) +//! }, +//! None, +//! )?) //! } //! } //! @@ -45,7 +71,14 @@ //! } //! } //! -//! fn test_lowercase() -> Result<(), LabeledError> { +//! // #[test] +//! fn test_examples() -> Result<(), ShellError> { +//! PluginTest::new("lowercase", LowercasePlugin.into())? +//! .test_command_examples(&Lowercase) +//! } +//! +//! // #[test] +//! fn test_lowercase() -> Result<(), ShellError> { //! let input = vec![Value::test_string("FooBar")].into_pipeline_data(None); //! let output = PluginTest::new("lowercase", LowercasePlugin.into())? //! .eval_with("lowercase", input)? @@ -60,6 +93,7 @@ //! Ok(()) //! } //! # +//! # test_examples().unwrap(); //! # test_lowercase().unwrap(); //! ``` diff --git a/crates/nu-plugin-test-support/src/plugin_test.rs b/crates/nu-plugin-test-support/src/plugin_test.rs index 9ce67a083c..cf6f2dddff 100644 --- a/crates/nu-plugin-test-support/src/plugin_test.rs +++ b/crates/nu-plugin-test-support/src/plugin_test.rs @@ -7,7 +7,7 @@ use nu_plugin::{Plugin, PluginCommand, PluginCustomValue, PluginSource}; use nu_protocol::{ debugger::WithoutDebug, engine::{EngineState, Stack, StateWorkingSet}, - report_error_new, LabeledError, PipelineData, PluginExample, ShellError, Span, Value, + report_error_new, Example, LabeledError, PipelineData, ShellError, Span, Value, }; use crate::{diff::diff_by_line, fake_register::fake_register}; @@ -186,20 +186,20 @@ impl PluginTest { /// /// ```rust,no_run /// # use nu_plugin_test_support::PluginTest; - /// # use nu_protocol::{ShellError, PluginExample, Value}; + /// # use nu_protocol::{ShellError, Example, Value}; /// # use nu_plugin::*; /// # fn test(MyPlugin: impl Plugin + Send + 'static) -> Result<(), ShellError> { /// PluginTest::new("my_plugin", MyPlugin.into())? /// .test_examples(&[ - /// PluginExample { - /// example: "my-command".into(), - /// description: "Run my-command".into(), + /// Example { + /// example: "my-command", + /// description: "Run my-command", /// result: Some(Value::test_string("my-command output")), /// }, /// ]) /// # } /// ``` - pub fn test_examples(&mut self, examples: &[PluginExample]) -> Result<(), ShellError> { + pub fn test_examples(&mut self, examples: &[Example]) -> Result<(), ShellError> { let mut failed = false; for example in examples { @@ -210,7 +210,7 @@ impl PluginTest { eprintln!("{} {}", bold.paint("Description:"), example.description); }; if let Some(expectation) = &example.result { - match self.eval(&example.example) { + match self.eval(example.example) { Ok(data) => { let mut value = data.into_value(Span::test_data()); @@ -270,6 +270,6 @@ impl PluginTest { &mut self, command: &impl PluginCommand, ) -> Result<(), ShellError> { - self.test_examples(&command.signature().examples) + self.test_examples(&command.examples()) } } diff --git a/crates/nu-plugin-test-support/tests/custom_value/mod.rs b/crates/nu-plugin-test-support/tests/custom_value/mod.rs index 1fad1e2f9e..12ee695b68 100644 --- a/crates/nu-plugin-test-support/tests/custom_value/mod.rs +++ b/crates/nu-plugin-test-support/tests/custom_value/mod.rs @@ -3,8 +3,7 @@ use std::cmp::Ordering; use nu_plugin::{EngineInterface, EvaluatedCall, Plugin, SimplePluginCommand}; use nu_plugin_test_support::PluginTest; use nu_protocol::{ - CustomValue, LabeledError, PipelineData, PluginExample, PluginSignature, ShellError, Span, - Type, Value, + CustomValue, Example, LabeledError, PipelineData, ShellError, Signature, Span, Type, Value, }; use serde::{Deserialize, Serialize}; @@ -58,14 +57,24 @@ impl Plugin for CustomU32Plugin { impl SimplePluginCommand for IntoU32 { type Plugin = CustomU32Plugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("into u32") - .input_output_type(Type::Int, Type::Custom("CustomU32".into())) - .plugin_examples(vec![PluginExample { - example: "340 | into u32".into(), - description: "Make a u32".into(), - result: Some(CustomU32(340).into_value(Span::test_data())), - }]) + fn name(&self) -> &str { + "into u32" + } + + fn usage(&self) -> &str { + "Convert a number to a 32-bit unsigned integer" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()).input_output_type(Type::Int, Type::Custom("CustomU32".into())) + } + + fn examples(&self) -> Vec { + vec![Example { + example: "340 | into u32", + description: "Make a u32", + result: Some(CustomU32(340).into_value(Span::test_data())), + }] } fn run( @@ -87,9 +96,16 @@ impl SimplePluginCommand for IntoU32 { impl SimplePluginCommand for IntoIntFromU32 { type Plugin = CustomU32Plugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("into int from u32") - .input_output_type(Type::Custom("CustomU32".into()), Type::Int) + fn name(&self) -> &str { + "into int from u32" + } + + fn usage(&self) -> &str { + "Turn a u32 back into a number" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()).input_output_type(Type::Custom("CustomU32".into()), Type::Int) } fn run( diff --git a/crates/nu-plugin-test-support/tests/hello/mod.rs b/crates/nu-plugin-test-support/tests/hello/mod.rs index b18404a24d..803b9620e8 100644 --- a/crates/nu-plugin-test-support/tests/hello/mod.rs +++ b/crates/nu-plugin-test-support/tests/hello/mod.rs @@ -2,7 +2,7 @@ use nu_plugin::*; use nu_plugin_test_support::PluginTest; -use nu_protocol::{LabeledError, PluginExample, PluginSignature, ShellError, Type, Value}; +use nu_protocol::{Example, LabeledError, ShellError, Signature, Type, Value}; struct HelloPlugin; struct Hello; @@ -16,14 +16,24 @@ impl Plugin for HelloPlugin { impl SimplePluginCommand for Hello { type Plugin = HelloPlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("hello") - .input_output_type(Type::Nothing, Type::String) - .plugin_examples(vec![PluginExample { - example: "hello".into(), - description: "Print a friendly greeting".into(), - result: Some(Value::test_string("Hello, World!")), - }]) + fn name(&self) -> &str { + "hello" + } + + fn usage(&self) -> &str { + "Print a friendly greeting" + } + + fn signature(&self) -> Signature { + Signature::build(PluginCommand::name(self)).input_output_type(Type::Nothing, Type::String) + } + + fn examples(&self) -> Vec { + vec![Example { + example: "hello", + description: "Print a friendly greeting", + result: Some(Value::test_string("Hello, World!")), + }] } fn run( @@ -44,9 +54,9 @@ fn test_specified_examples() -> Result<(), ShellError> { #[test] fn test_an_error_causing_example() -> Result<(), ShellError> { - let result = PluginTest::new("hello", HelloPlugin.into())?.test_examples(&[PluginExample { - example: "hello --unknown-flag".into(), - description: "Run hello with an unknown flag".into(), + let result = PluginTest::new("hello", HelloPlugin.into())?.test_examples(&[Example { + example: "hello --unknown-flag", + description: "Run hello with an unknown flag", result: Some(Value::test_string("Hello, World!")), }]); assert!(result.is_err()); @@ -55,9 +65,9 @@ fn test_an_error_causing_example() -> Result<(), ShellError> { #[test] fn test_an_example_with_the_wrong_result() -> Result<(), ShellError> { - let result = PluginTest::new("hello", HelloPlugin.into())?.test_examples(&[PluginExample { - example: "hello".into(), - description: "Run hello but the example result is wrong".into(), + let result = PluginTest::new("hello", HelloPlugin.into())?.test_examples(&[Example { + example: "hello", + description: "Run hello but the example result is wrong", result: Some(Value::test_string("Goodbye, World!")), }]); assert!(result.is_err()); diff --git a/crates/nu-plugin-test-support/tests/lowercase/mod.rs b/crates/nu-plugin-test-support/tests/lowercase/mod.rs index 95c30427f3..25a6063dc0 100644 --- a/crates/nu-plugin-test-support/tests/lowercase/mod.rs +++ b/crates/nu-plugin-test-support/tests/lowercase/mod.rs @@ -1,8 +1,8 @@ use nu_plugin::*; use nu_plugin_test_support::PluginTest; use nu_protocol::{ - IntoInterruptiblePipelineData, LabeledError, PipelineData, PluginExample, PluginSignature, - ShellError, Span, Type, Value, + Example, IntoInterruptiblePipelineData, LabeledError, PipelineData, ShellError, Signature, + Span, Type, Value, }; struct LowercasePlugin; @@ -11,21 +11,30 @@ struct Lowercase; impl PluginCommand for Lowercase { type Plugin = LowercasePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("lowercase") - .usage("Convert each string in a stream to lowercase") - .input_output_type( - Type::List(Type::String.into()), - Type::List(Type::String.into()), - ) - .plugin_examples(vec![PluginExample { - example: r#"[Hello wORLD] | lowercase"#.into(), - description: "Lowercase a list of strings".into(), - result: Some(Value::test_list(vec![ - Value::test_string("hello"), - Value::test_string("world"), - ])), - }]) + fn name(&self) -> &str { + "lowercase" + } + + fn usage(&self) -> &str { + "Convert each string in a stream to lowercase" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()).input_output_type( + Type::List(Type::String.into()), + Type::List(Type::String.into()), + ) + } + + fn examples(&self) -> Vec { + vec![Example { + example: r#"[Hello wORLD] | lowercase"#, + description: "Lowercase a list of strings", + result: Some(Value::test_list(vec![ + Value::test_string("hello"), + Value::test_string("world"), + ])), + }] } fn run( diff --git a/crates/nu-plugin/src/lib.rs b/crates/nu-plugin/src/lib.rs index b051195093..d5c6c27157 100644 --- a/crates/nu-plugin/src/lib.rs +++ b/crates/nu-plugin/src/lib.rs @@ -17,8 +17,8 @@ //! //! ```rust,no_run //! use nu_plugin::{EvaluatedCall, MsgPackSerializer, serve_plugin}; -//! use nu_plugin::{Plugin, PluginCommand, SimplePluginCommand, EngineInterface}; -//! use nu_protocol::{PluginSignature, LabeledError, Value}; +//! use nu_plugin::{EngineInterface, Plugin, PluginCommand, SimplePluginCommand}; +//! use nu_protocol::{LabeledError, Signature, Value}; //! //! struct MyPlugin; //! struct MyCommand; @@ -32,7 +32,15 @@ //! impl SimplePluginCommand for MyCommand { //! type Plugin = MyPlugin; //! -//! fn signature(&self) -> PluginSignature { +//! fn name(&self) -> &str { +//! "my-command" +//! } +//! +//! fn usage(&self) -> &str { +//! todo!(); +//! } +//! +//! fn signature(&self) -> Signature { //! todo!(); //! } //! @@ -71,9 +79,10 @@ pub use serializers::{json::JsonSerializer, msgpack::MsgPackSerializer}; // Used by other nu crates. #[doc(hidden)] pub use plugin::{ - get_signature, serve_plugin_io, EngineInterfaceManager, GetPlugin, Interface, InterfaceManager, - PersistentPlugin, PluginDeclaration, PluginExecutionCommandContext, PluginExecutionContext, - PluginInterface, PluginInterfaceManager, PluginSource, ServePluginError, + create_plugin_signature, get_signature, serve_plugin_io, EngineInterfaceManager, GetPlugin, + Interface, InterfaceManager, PersistentPlugin, PluginDeclaration, + PluginExecutionCommandContext, PluginExecutionContext, PluginInterface, PluginInterfaceManager, + PluginSource, ServePluginError, }; #[doc(hidden)] pub use protocol::{PluginCustomValue, PluginInput, PluginOutput}; diff --git a/crates/nu-plugin/src/plugin/command.rs b/crates/nu-plugin/src/plugin/command.rs index 4e82056d35..bfad385d6c 100644 --- a/crates/nu-plugin/src/plugin/command.rs +++ b/crates/nu-plugin/src/plugin/command.rs @@ -1,4 +1,6 @@ -use nu_protocol::{LabeledError, PipelineData, PluginSignature, Value}; +use nu_protocol::{ + Example, LabeledError, PipelineData, PluginExample, PluginSignature, Signature, Value, +}; use crate::{EngineInterface, EvaluatedCall, Plugin}; @@ -19,16 +21,23 @@ use crate::{EngineInterface, EvaluatedCall, Plugin}; /// Basic usage: /// ``` /// # use nu_plugin::*; -/// # use nu_protocol::{PluginSignature, PipelineData, Type, Value, LabeledError}; +/// # use nu_protocol::{Signature, PipelineData, Type, Value, LabeledError}; /// struct LowercasePlugin; /// struct Lowercase; /// /// impl PluginCommand for Lowercase { /// type Plugin = LowercasePlugin; /// -/// fn signature(&self) -> PluginSignature { -/// PluginSignature::build("lowercase") -/// .usage("Convert each string in a stream to lowercase") +/// fn name(&self) -> &str { +/// "lowercase" +/// } +/// +/// fn usage(&self) -> &str { +/// "Convert each string in a stream to lowercase" +/// } +/// +/// fn signature(&self) -> Signature { +/// Signature::build(PluginCommand::name(self)) /// .input_output_type(Type::List(Type::String.into()), Type::List(Type::String.into())) /// } /// @@ -60,18 +69,62 @@ use crate::{EngineInterface, EvaluatedCall, Plugin}; /// # } /// ``` pub trait PluginCommand: Sync { - /// The type of plugin this command runs on + /// The type of plugin this command runs on. /// /// Since [`.run()`] takes a reference to the plugin, it is necessary to define the type of /// plugin that the command expects here. type Plugin: Plugin; - /// The signature of the plugin command + /// The name of the command from within Nu. /// - /// These are aggregated from the [`Plugin`] and sent to the engine on `register`. - fn signature(&self) -> PluginSignature; + /// In case this contains spaces, it will be treated as a subcommand. + fn name(&self) -> &str; - /// Perform the actual behavior of the plugin command + /// The signature of the command. + /// + /// This defines the arguments and input/output types of the command. + fn signature(&self) -> Signature; + + /// A brief description of usage for the command. + /// + /// This should be short enough to fit in completion menus. + fn usage(&self) -> &str; + + /// Additional documentation for usage of the command. + /// + /// This is optional - any arguments documented by [`.signature()`] will be shown in the help + /// page automatically. However, this can be useful for explaining things that would be too + /// brief to include in [`.usage()`] and may span multiple lines. + fn extra_usage(&self) -> &str { + "" + } + + /// Search terms to help users find the command. + /// + /// A search query matching any of these search keywords, e.g. on `help --find`, will also + /// show this command as a result. This may be used to suggest this command as a replacement + /// for common system commands, or based alternate names for the functionality this command + /// provides. + /// + /// For example, a `fold` command might mention `reduce` in its search terms. + fn search_terms(&self) -> Vec<&str> { + vec![] + } + + /// Examples, in Nu, of how the command might be used. + /// + /// The examples are not restricted to only including this command, and may demonstrate + /// pipelines using the command. A `result` may optionally be provided to show users what the + /// command would return. + /// + /// `PluginTest::test_command_examples()` from the + /// [`nu-plugin-test-support`](https://docs.rs/nu-plugin-test-support) crate can be used in + /// plugin tests to automatically test that examples produce the `result`s as specified. + fn examples(&self) -> Vec { + vec![] + } + + /// Perform the actual behavior of the plugin command. /// /// The behavior of the plugin is defined by the implementation of this method. When Nushell /// invoked the plugin [`serve_plugin`](crate::serve_plugin) will call this method and print the @@ -109,15 +162,23 @@ pub trait PluginCommand: Sync { /// Basic usage: /// ``` /// # use nu_plugin::*; -/// # use nu_protocol::{PluginSignature, Type, Value, LabeledError}; +/// # use nu_protocol::{LabeledError, Signature, Type, Value}; /// struct HelloPlugin; /// struct Hello; /// /// impl SimplePluginCommand for Hello { /// type Plugin = HelloPlugin; /// -/// fn signature(&self) -> PluginSignature { -/// PluginSignature::build("hello") +/// fn name(&self) -> &str { +/// "hello" +/// } +/// +/// fn usage(&self) -> &str { +/// "Every programmer's favorite greeting" +/// } +/// +/// fn signature(&self) -> Signature { +/// Signature::build(PluginCommand::name(self)) /// .input_output_type(Type::Nothing, Type::String) /// } /// @@ -143,18 +204,62 @@ pub trait PluginCommand: Sync { /// # } /// ``` pub trait SimplePluginCommand: Sync { - /// The type of plugin this command runs on + /// The type of plugin this command runs on. /// /// Since [`.run()`] takes a reference to the plugin, it is necessary to define the type of /// plugin that the command expects here. type Plugin: Plugin; - /// The signature of the plugin command + /// The name of the command from within Nu. /// - /// These are aggregated from the [`Plugin`] and sent to the engine on `register`. - fn signature(&self) -> PluginSignature; + /// In case this contains spaces, it will be treated as a subcommand. + fn name(&self) -> &str; - /// Perform the actual behavior of the plugin command + /// The signature of the command. + /// + /// This defines the arguments and input/output types of the command. + fn signature(&self) -> Signature; + + /// A brief description of usage for the command. + /// + /// This should be short enough to fit in completion menus. + fn usage(&self) -> &str; + + /// Additional documentation for usage of the command. + /// + /// This is optional - any arguments documented by [`.signature()`] will be shown in the help + /// page automatically. However, this can be useful for explaining things that would be too + /// brief to include in [`.usage()`] and may span multiple lines. + fn extra_usage(&self) -> &str { + "" + } + + /// Search terms to help users find the command. + /// + /// A search query matching any of these search keywords, e.g. on `help --find`, will also + /// show this command as a result. This may be used to suggest this command as a replacement + /// for common system commands, or based alternate names for the functionality this command + /// provides. + /// + /// For example, a `fold` command might mention `reduce` in its search terms. + fn search_terms(&self) -> Vec<&str> { + vec![] + } + + /// Examples, in Nu, of how the command might be used. + /// + /// The examples are not restricted to only including this command, and may demonstrate + /// pipelines using the command. A `result` may optionally be provided to show users what the + /// command would return. + /// + /// `PluginTest::test_command_examples()` from the + /// [`nu-plugin-test-support`](https://docs.rs/nu-plugin-test-support) crate can be used in + /// plugin tests to automatically test that examples produce the `result`s as specified. + fn examples(&self) -> Vec { + vec![] + } + + /// Perform the actual behavior of the plugin command. /// /// The behavior of the plugin is defined by the implementation of this method. When Nushell /// invoked the plugin [`serve_plugin`](crate::serve_plugin) will call this method and print the @@ -185,8 +290,16 @@ where { type Plugin = ::Plugin; - fn signature(&self) -> PluginSignature { - ::signature(self) + fn examples(&self) -> Vec { + ::examples(self) + } + + fn extra_usage(&self) -> &str { + ::extra_usage(self) + } + + fn name(&self) -> &str { + ::name(self) } fn run( @@ -204,4 +317,45 @@ where ::run(self, plugin, engine, call, &input_value) .map(|value| PipelineData::Value(value, None)) } + + fn search_terms(&self) -> Vec<&str> { + ::search_terms(self) + } + + fn signature(&self) -> Signature { + ::signature(self) + } + + fn usage(&self) -> &str { + ::usage(self) + } +} + +/// Build a [`PluginSignature`] from the signature-related methods on [`PluginCommand`]. +/// +/// This is sent to the engine on `register`. +/// +/// This is not a public API. +#[doc(hidden)] +pub fn create_plugin_signature(command: &(impl PluginCommand + ?Sized)) -> PluginSignature { + PluginSignature::new( + // Add results of trait methods to signature + command + .signature() + .usage(command.usage()) + .extra_usage(command.extra_usage()) + .search_terms( + command + .search_terms() + .into_iter() + .map(String::from) + .collect(), + ), + // Convert `Example`s to `PluginExample`s + command + .examples() + .into_iter() + .map(PluginExample::from) + .collect(), + ) } diff --git a/crates/nu-plugin/src/plugin/interface/engine/tests.rs b/crates/nu-plugin/src/plugin/interface/engine/tests.rs index e2f76811f7..fcb0a9ac5b 100644 --- a/crates/nu-plugin/src/plugin/interface/engine/tests.rs +++ b/crates/nu-plugin/src/plugin/interface/engine/tests.rs @@ -11,7 +11,7 @@ use crate::{ }; use nu_protocol::{ engine::Closure, Config, CustomValue, IntoInterruptiblePipelineData, LabeledError, - PipelineData, PluginExample, PluginSignature, ShellError, Span, Spanned, Value, + PipelineData, PluginExample, PluginSignature, ShellError, Signature, Span, Spanned, Value, }; use std::{ collections::HashMap, @@ -786,15 +786,16 @@ fn interface_write_signature() -> Result<(), ShellError> { fn interface_write_signature_custom_value() -> Result<(), ShellError> { let test = TestCase::new(); let interface = test.engine().interface_for_context(38); - let signatures = vec![PluginSignature::build("test command").plugin_examples(vec![ - PluginExample { + let signatures = vec![PluginSignature::new( + Signature::build("test command"), + vec![PluginExample { example: "test command".into(), description: "a test".into(), result: Some(Value::test_custom_value(Box::new( expected_test_custom_value(), ))), - }, - ])]; + }], + )]; interface.write_signature(signatures.clone())?; let written = test.next_written().expect("nothing written"); diff --git a/crates/nu-plugin/src/plugin/mod.rs b/crates/nu-plugin/src/plugin/mod.rs index 9bea8dc5f1..40cecd9011 100644 --- a/crates/nu-plugin/src/plugin/mod.rs +++ b/crates/nu-plugin/src/plugin/mod.rs @@ -3,15 +3,7 @@ use crate::{ protocol::{CallInfo, CustomValueOp, PluginCustomValue, PluginInput, PluginOutput}, EncodingType, }; -use nu_engine::documentation::get_flags_section; -use nu_protocol::{ - ast::Operator, CustomValue, IntoSpanned, LabeledError, PipelineData, PluginSignature, - ShellError, Spanned, Value, -}; -#[cfg(unix)] -use std::os::unix::process::CommandExt; -#[cfg(windows)] -use std::os::windows::process::CommandExt; + use std::{ cmp::Ordering, collections::HashMap, @@ -19,14 +11,28 @@ use std::{ ffi::OsStr, fmt::Write, io::{BufReader, Read, Write as WriteTrait}, + ops::Deref, path::Path, process::{Child, ChildStdout, Command as CommandSys, Stdio}, - sync::mpsc::TrySendError, - sync::{mpsc, Arc, Mutex}, + sync::{ + mpsc::{self, TrySendError}, + Arc, Mutex, + }, thread, }; + +use nu_engine::documentation::get_flags_section; +use nu_protocol::{ + ast::Operator, CustomValue, IntoSpanned, LabeledError, PipelineData, PluginSignature, + ShellError, Spanned, Value, +}; use thiserror::Error; +#[cfg(unix)] +use std::os::unix::process::CommandExt; +#[cfg(windows)] +use std::os::windows::process::CommandExt; + use self::gc::PluginGc; pub use self::interface::{PluginRead, PluginWrite}; @@ -38,7 +44,7 @@ mod interface; mod persistent; mod source; -pub use command::{PluginCommand, SimplePluginCommand}; +pub use command::{create_plugin_signature, PluginCommand, SimplePluginCommand}; pub use declaration::PluginDeclaration; pub use interface::{ EngineInterface, EngineInterfaceManager, Interface, InterfaceManager, PluginInterface, @@ -229,7 +235,7 @@ where /// Basic usage: /// ``` /// # use nu_plugin::*; -/// # use nu_protocol::{PluginSignature, LabeledError, Type, Value}; +/// # use nu_protocol::{LabeledError, Signature, Type, Value}; /// struct HelloPlugin; /// struct Hello; /// @@ -242,8 +248,16 @@ where /// impl SimplePluginCommand for Hello { /// type Plugin = HelloPlugin; /// -/// fn signature(&self) -> PluginSignature { -/// PluginSignature::build("hello") +/// fn name(&self) -> &str { +/// "hello" +/// } +/// +/// fn usage(&self) -> &str { +/// "Every programmer's favorite greeting" +/// } +/// +/// fn signature(&self) -> Signature { +/// Signature::build(PluginCommand::name(self)) /// .input_output_type(Type::Nothing, Type::String) /// } /// @@ -556,11 +570,11 @@ where let mut commands: HashMap = HashMap::new(); for command in plugin.commands() { - if let Some(previous) = commands.insert(command.signature().sig.name.clone(), command) { + if let Some(previous) = commands.insert(command.name().into(), command) { eprintln!( "Plugin `{plugin_name}` warning: command `{}` shadowed by another command with the \ - same name. Check your command signatures", - previous.signature().sig.name + same name. Check your commands' `name()` methods", + previous.name() ); } } @@ -636,7 +650,7 @@ where ReceivedPluginCall::Signature { engine } => { let sigs = commands .values() - .map(|command| command.signature()) + .map(|command| create_plugin_signature(command.deref())) .collect(); engine.write_signature(sigs).try_to_report(&engine)?; } @@ -752,23 +766,22 @@ fn print_help(plugin: &impl Plugin, encoder: impl PluginEncoder) { plugin.commands().into_iter().for_each(|command| { let signature = command.signature(); - let res = write!(help, "\nCommand: {}", signature.sig.name) - .and_then(|_| writeln!(help, "\nUsage:\n > {}", signature.sig.usage)) + let res = write!(help, "\nCommand: {}", command.name()) + .and_then(|_| writeln!(help, "\nUsage:\n > {}", command.usage())) .and_then(|_| { - if !signature.sig.extra_usage.is_empty() { - writeln!(help, "\nExtra usage:\n > {}", signature.sig.extra_usage) + if !command.extra_usage().is_empty() { + writeln!(help, "\nExtra usage:\n > {}", command.extra_usage()) } else { Ok(()) } }) .and_then(|_| { - let flags = get_flags_section(None, &signature.sig, |v| format!("{:#?}", v)); + let flags = get_flags_section(None, &signature, |v| format!("{:#?}", v)); write!(help, "{flags}") }) .and_then(|_| writeln!(help, "\nParameters:")) .and_then(|_| { signature - .sig .required_positional .iter() .try_for_each(|positional| { @@ -781,7 +794,6 @@ fn print_help(plugin: &impl Plugin, encoder: impl PluginEncoder) { }) .and_then(|_| { signature - .sig .optional_positional .iter() .try_for_each(|positional| { @@ -793,7 +805,7 @@ fn print_help(plugin: &impl Plugin, encoder: impl PluginEncoder) { }) }) .and_then(|_| { - if let Some(rest_positional) = &signature.sig.rest_positional { + if let Some(rest_positional) = &signature.rest_positional { writeln!( help, " ...{} <{}>: {}", diff --git a/crates/nu-plugin/src/serializers/tests.rs b/crates/nu-plugin/src/serializers/tests.rs index 7d69c02025..505c8fe915 100644 --- a/crates/nu-plugin/src/serializers/tests.rs +++ b/crates/nu-plugin/src/serializers/tests.rs @@ -5,7 +5,9 @@ macro_rules! generate_tests { PluginCallResponse, PluginCustomValue, PluginInput, PluginOption, PluginOutput, StreamData, StreamMessage, }; - use nu_protocol::{LabeledError, PluginSignature, Span, Spanned, SyntaxShape, Value}; + use nu_protocol::{ + LabeledError, PluginSignature, Signature, Span, Spanned, SyntaxShape, Value, + }; #[test] fn decode_eof() { @@ -211,17 +213,20 @@ macro_rules! generate_tests { #[test] fn response_round_trip_signature() { - let signature = PluginSignature::build("nu-plugin") - .required("first", SyntaxShape::String, "first required") - .required("second", SyntaxShape::Int, "second required") - .required_named("first-named", SyntaxShape::String, "first named", Some('f')) - .required_named( - "second-named", - SyntaxShape::String, - "second named", - Some('s'), - ) - .rest("remaining", SyntaxShape::Int, "remaining"); + let signature = PluginSignature::new( + Signature::build("nu-plugin") + .required("first", SyntaxShape::String, "first required") + .required("second", SyntaxShape::Int, "second required") + .required_named("first-named", SyntaxShape::String, "first named", Some('f')) + .required_named( + "second-named", + SyntaxShape::String, + "second named", + Some('s'), + ) + .rest("remaining", SyntaxShape::Int, "remaining"), + vec![], + ); let response = PluginCallResponse::Signature(vec![signature.clone()]); let output = PluginOutput::CallResponse(3, response); diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index 5ff56c3f1a..7117abf88b 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -530,11 +530,7 @@ impl EngineState { let examples = decl .examples() .into_iter() - .map(|eg| PluginExample { - example: eg.example.into(), - description: eg.description.into(), - result: eg.result, - }) + .map(PluginExample::from) .collect(); let sig_with_examples = PluginSignature::new(sig, examples); serde_json::to_string_pretty(&sig_with_examples) diff --git a/crates/nu-protocol/src/example.rs b/crates/nu-protocol/src/example.rs index 9072b40ea2..becb9074a6 100644 --- a/crates/nu-protocol/src/example.rs +++ b/crates/nu-protocol/src/example.rs @@ -19,3 +19,13 @@ pub struct PluginExample { pub description: String, pub result: Option, } + +impl From> for PluginExample { + fn from(value: Example) -> Self { + PluginExample { + example: value.example.into(), + description: value.description.into(), + result: value.result, + } + } +} diff --git a/crates/nu-protocol/src/plugin/signature.rs b/crates/nu-protocol/src/plugin/signature.rs index d92736abdf..1e4802cf56 100644 --- a/crates/nu-protocol/src/plugin/signature.rs +++ b/crates/nu-protocol/src/plugin/signature.rs @@ -1,7 +1,4 @@ -use crate::{ - engine::Command, BlockId, Category, Flag, PluginExample, PositionalArg, Signature, SyntaxShape, - Type, -}; +use crate::{PluginExample, Signature}; use serde::{Deserialize, Serialize}; /// A simple wrapper for Signature that includes examples. @@ -16,210 +13,9 @@ impl PluginSignature { Self { sig, examples } } - /// Add a default help option to a signature - pub fn add_help(mut self) -> PluginSignature { - self.sig = self.sig.add_help(); - self - } - /// Build an internal signature with default help option pub fn build(name: impl Into) -> PluginSignature { let sig = Signature::new(name.into()).add_help(); Self::new(sig, vec![]) } - - /// Add a description to the signature - pub fn usage(mut self, msg: impl Into) -> PluginSignature { - self.sig = self.sig.usage(msg); - self - } - - /// Add an extra description to the signature - pub fn extra_usage(mut self, msg: impl Into) -> PluginSignature { - self.sig = self.sig.extra_usage(msg); - self - } - - /// Add search terms to the signature - pub fn search_terms(mut self, terms: Vec) -> PluginSignature { - self.sig = self.sig.search_terms(terms); - self - } - - /// Update signature's fields from a Command trait implementation - pub fn update_from_command(mut self, command: &dyn Command) -> PluginSignature { - self.sig = self.sig.update_from_command(command); - self - } - - /// Allow unknown signature parameters - pub fn allows_unknown_args(mut self) -> PluginSignature { - self.sig = self.sig.allows_unknown_args(); - self - } - - /// Add a required positional argument to the signature - pub fn required( - mut self, - name: impl Into, - shape: impl Into, - desc: impl Into, - ) -> PluginSignature { - self.sig = self.sig.required(name, shape, desc); - self - } - - /// Add an optional positional argument to the signature - pub fn optional( - mut self, - name: impl Into, - shape: impl Into, - desc: impl Into, - ) -> PluginSignature { - self.sig = self.sig.optional(name, shape, desc); - self - } - - pub fn rest( - mut self, - name: &str, - shape: impl Into, - desc: impl Into, - ) -> PluginSignature { - self.sig = self.sig.rest(name, shape, desc); - self - } - - /// Is this command capable of operating on its input via cell paths? - pub fn operates_on_cell_paths(&self) -> bool { - self.sig.operates_on_cell_paths() - } - - /// Add an optional named flag argument to the signature - pub fn named( - mut self, - name: impl Into, - shape: impl Into, - desc: impl Into, - short: Option, - ) -> PluginSignature { - self.sig = self.sig.named(name, shape, desc, short); - self - } - - /// Add a required named flag argument to the signature - pub fn required_named( - mut self, - name: impl Into, - shape: impl Into, - desc: impl Into, - short: Option, - ) -> PluginSignature { - self.sig = self.sig.required_named(name, shape, desc, short); - self - } - - /// Add a switch to the signature - pub fn switch( - mut self, - name: impl Into, - desc: impl Into, - short: Option, - ) -> PluginSignature { - self.sig = self.sig.switch(name, desc, short); - self - } - - /// Changes the input type of the command signature - pub fn input_output_type(mut self, input_type: Type, output_type: Type) -> PluginSignature { - self.sig.input_output_types.push((input_type, output_type)); - self - } - - /// Set the input-output type signature variants of the command - pub fn input_output_types(mut self, input_output_types: Vec<(Type, Type)>) -> PluginSignature { - self.sig = self.sig.input_output_types(input_output_types); - self - } - - /// Changes the signature category - pub fn category(mut self, category: Category) -> PluginSignature { - self.sig = self.sig.category(category); - self - } - - /// Sets that signature will create a scope as it parses - pub fn creates_scope(mut self) -> PluginSignature { - self.sig = self.sig.creates_scope(); - self - } - - // Is it allowed for the type signature to feature a variant that has no corresponding example? - pub fn allow_variants_without_examples(mut self, allow: bool) -> PluginSignature { - self.sig = self.sig.allow_variants_without_examples(allow); - self - } - - pub fn call_signature(&self) -> String { - self.sig.call_signature() - } - - /// Get list of the short-hand flags - pub fn get_shorts(&self) -> Vec { - self.sig.get_shorts() - } - - /// Get list of the long-hand flags - pub fn get_names(&self) -> Vec<&str> { - self.sig.get_names() - } - - pub fn get_positional(&self, position: usize) -> Option { - self.sig.get_positional(position) - } - - pub fn num_positionals(&self) -> usize { - self.sig.num_positionals() - } - - pub fn num_positionals_after(&self, idx: usize) -> usize { - self.sig.num_positionals_after(idx) - } - - /// Find the matching long flag - pub fn get_long_flag(&self, name: &str) -> Option { - self.sig.get_long_flag(name) - } - - /// Find the matching long flag - pub fn get_short_flag(&self, short: char) -> Option { - self.sig.get_short_flag(short) - } - - /// Set the filter flag for the signature - pub fn filter(mut self) -> PluginSignature { - self.sig = self.sig.filter(); - self - } - - /// Create a placeholder implementation of Command as a way to predeclare a definition's - /// signature so other definitions can see it. This placeholder is later replaced with the - /// full definition in a second pass of the parser. - pub fn predeclare(self) -> Box { - self.sig.predeclare() - } - - /// Combines a signature and a block into a runnable block - pub fn into_block_command(self, block_id: BlockId) -> Box { - self.sig.into_block_command(block_id) - } - - pub fn formatted_flags(self) -> String { - self.sig.formatted_flags() - } - - pub fn plugin_examples(mut self, examples: Vec) -> PluginSignature { - self.examples = examples; - self - } } diff --git a/crates/nu_plugin_custom_values/src/drop_check.rs b/crates/nu_plugin_custom_values/src/drop_check.rs index 8a29d54641..b12f5e837c 100644 --- a/crates/nu_plugin_custom_values/src/drop_check.rs +++ b/crates/nu_plugin_custom_values/src/drop_check.rs @@ -1,8 +1,7 @@ use crate::CustomValuePlugin; use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; use nu_protocol::{ - record, Category, CustomValue, LabeledError, PluginSignature, ShellError, Span, SyntaxShape, - Value, + record, Category, CustomValue, LabeledError, ShellError, Signature, Span, SyntaxShape, Value, }; use serde::{Deserialize, Serialize}; @@ -59,9 +58,16 @@ pub struct DropCheck; impl SimplePluginCommand for DropCheck { type Plugin = CustomValuePlugin; - fn signature(&self) -> nu_protocol::PluginSignature { - PluginSignature::build("custom-value drop-check") - .usage("Generates a custom value that prints a message when dropped") + fn name(&self) -> &str { + "custom-value drop-check" + } + + fn usage(&self) -> &str { + "Generates a custom value that prints a message when dropped" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .required("msg", SyntaxShape::String, "the message to print on drop") .category(Category::Experimental) } diff --git a/crates/nu_plugin_custom_values/src/generate.rs b/crates/nu_plugin_custom_values/src/generate.rs index 975ceaf048..1176f9ca9a 100644 --- a/crates/nu_plugin_custom_values/src/generate.rs +++ b/crates/nu_plugin_custom_values/src/generate.rs @@ -1,21 +1,30 @@ use crate::{cool_custom_value::CoolCustomValue, CustomValuePlugin}; use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; -use nu_protocol::{Category, LabeledError, PluginExample, PluginSignature, Span, Value}; +use nu_protocol::{Category, Example, LabeledError, Signature, Span, Value}; pub struct Generate; impl SimplePluginCommand for Generate { type Plugin = CustomValuePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("custom-value generate") - .usage("PluginSignature for a plugin that generates a custom value") - .category(Category::Experimental) - .plugin_examples(vec![PluginExample { - example: "custom-value generate".into(), - description: "Generate a new CoolCustomValue".into(), - result: Some(CoolCustomValue::new("abc").into_value(Span::test_data())), - }]) + fn name(&self) -> &str { + "custom-value generate" + } + + fn usage(&self) -> &str { + "PluginSignature for a plugin that generates a custom value" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()).category(Category::Experimental) + } + + fn examples(&self) -> Vec { + vec![Example { + example: "custom-value generate", + description: "Generate a new CoolCustomValue", + result: Some(CoolCustomValue::new("abc").into_value(Span::test_data())), + }] } fn run( diff --git a/crates/nu_plugin_custom_values/src/generate2.rs b/crates/nu_plugin_custom_values/src/generate2.rs index 1a1f89f9a3..1f5f2e8ef3 100644 --- a/crates/nu_plugin_custom_values/src/generate2.rs +++ b/crates/nu_plugin_custom_values/src/generate2.rs @@ -1,35 +1,43 @@ use crate::{second_custom_value::SecondCustomValue, CustomValuePlugin}; use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; -use nu_protocol::{ - Category, LabeledError, PluginExample, PluginSignature, Span, SyntaxShape, Value, -}; +use nu_protocol::{Category, Example, LabeledError, Signature, Span, SyntaxShape, Value}; pub struct Generate2; impl SimplePluginCommand for Generate2 { type Plugin = CustomValuePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("custom-value generate2") - .usage("PluginSignature for a plugin that generates a different custom value") + fn name(&self) -> &str { + "custom-value generate2" + } + + fn usage(&self) -> &str { + "PluginSignature for a plugin that generates a different custom value" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .optional( "closure", SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), "An optional closure to pass the custom value to", ) .category(Category::Experimental) - .plugin_examples(vec![ - PluginExample { - example: "custom-value generate2".into(), - description: "Generate a new SecondCustomValue".into(), - result: Some(SecondCustomValue::new("xyz").into_value(Span::test_data())), - }, - PluginExample { - example: "custom-value generate2 { print }".into(), - description: "Generate a new SecondCustomValue and pass it to a closure".into(), - result: None, - }, - ]) + } + + fn examples(&self) -> Vec { + vec![ + Example { + example: "custom-value generate2", + description: "Generate a new SecondCustomValue", + result: Some(SecondCustomValue::new("xyz").into_value(Span::test_data())), + }, + Example { + example: "custom-value generate2 { print }", + description: "Generate a new SecondCustomValue and pass it to a closure", + result: None, + }, + ] } fn run( diff --git a/crates/nu_plugin_custom_values/src/update.rs b/crates/nu_plugin_custom_values/src/update.rs index 835d7291df..7c4c5e4960 100644 --- a/crates/nu_plugin_custom_values/src/update.rs +++ b/crates/nu_plugin_custom_values/src/update.rs @@ -2,31 +2,38 @@ use crate::{ cool_custom_value::CoolCustomValue, second_custom_value::SecondCustomValue, CustomValuePlugin, }; use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; -use nu_protocol::{ - Category, LabeledError, PluginExample, PluginSignature, ShellError, Span, Value, -}; +use nu_protocol::{Category, Example, LabeledError, ShellError, Signature, Span, Value}; pub struct Update; impl SimplePluginCommand for Update { type Plugin = CustomValuePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("custom-value update") - .usage("PluginSignature for a plugin that updates a custom value") - .category(Category::Experimental) - .plugin_examples(vec![ - PluginExample { - example: "custom-value generate | custom-value update".into(), - description: "Update a CoolCustomValue".into(), - result: Some(CoolCustomValue::new("abcxyz").into_value(Span::test_data())), - }, - PluginExample { - example: "custom-value generate2 | custom-value update".into(), - description: "Update a SecondCustomValue".into(), - result: Some(SecondCustomValue::new("xyzabc").into_value(Span::test_data())), - }, - ]) + fn name(&self) -> &str { + "custom-value update" + } + + fn usage(&self) -> &str { + "PluginSignature for a plugin that updates a custom value" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()).category(Category::Experimental) + } + + fn examples(&self) -> Vec { + vec![ + Example { + example: "custom-value generate | custom-value update", + description: "Update a CoolCustomValue", + result: Some(CoolCustomValue::new("abcxyz").into_value(Span::test_data())), + }, + Example { + example: "custom-value generate2 | custom-value update", + description: "Update a SecondCustomValue", + result: Some(SecondCustomValue::new("xyzabc").into_value(Span::test_data())), + }, + ] } fn run( diff --git a/crates/nu_plugin_custom_values/src/update_arg.rs b/crates/nu_plugin_custom_values/src/update_arg.rs index 5493059dd8..adbe64773c 100644 --- a/crates/nu_plugin_custom_values/src/update_arg.rs +++ b/crates/nu_plugin_custom_values/src/update_arg.rs @@ -1,15 +1,22 @@ use crate::{update::Update, CustomValuePlugin}; use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; -use nu_protocol::{Category, LabeledError, PluginSignature, SyntaxShape, Value}; +use nu_protocol::{Category, LabeledError, Signature, SyntaxShape, Value}; pub struct UpdateArg; impl SimplePluginCommand for UpdateArg { type Plugin = CustomValuePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("custom-value update-arg") - .usage("PluginSignature for a plugin that updates a custom value as an argument") + fn name(&self) -> &str { + "custom-value update-arg" + } + + fn usage(&self) -> &str { + "Updates a custom value as an argument" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .required( "custom_value", SyntaxShape::Any, diff --git a/crates/nu_plugin_example/src/commands/collect_external.rs b/crates/nu_plugin_example/src/commands/collect_external.rs index 8ab70bdd9e..e5c8c61f2e 100644 --- a/crates/nu_plugin_example/src/commands/collect_external.rs +++ b/crates/nu_plugin_example/src/commands/collect_external.rs @@ -1,35 +1,48 @@ use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand}; use nu_protocol::{ - Category, LabeledError, PipelineData, PluginExample, PluginSignature, RawStream, Type, Value, + Category, Example, LabeledError, PipelineData, RawStream, Signature, Type, Value, }; -use crate::Example; +use crate::ExamplePlugin; /// `> | example collect-external` pub struct CollectExternal; impl PluginCommand for CollectExternal { - type Plugin = Example; + type Plugin = ExamplePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("example collect-external") - .usage("Example transformer to raw external stream") - .search_terms(vec!["example".into()]) + fn name(&self) -> &str { + "example collect-external" + } + + fn usage(&self) -> &str { + "Example transformer to raw external stream" + } + + fn search_terms(&self) -> Vec<&str> { + vec!["example"] + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .input_output_types(vec![ (Type::List(Type::String.into()), Type::String), (Type::List(Type::Binary.into()), Type::Binary), ]) - .plugin_examples(vec![PluginExample { - example: "[a b] | example collect-external".into(), - description: "collect strings into one stream".into(), - result: Some(Value::test_string("ab")), - }]) .category(Category::Experimental) } + fn examples(&self) -> Vec { + vec![Example { + example: "[a b] | example collect-external", + description: "collect strings into one stream", + result: Some(Value::test_string("ab")), + }] + } + fn run( &self, - _plugin: &Example, + _plugin: &ExamplePlugin, _engine: &EngineInterface, call: &EvaluatedCall, input: PipelineData, @@ -55,5 +68,5 @@ impl PluginCommand for CollectExternal { #[test] fn test_examples() -> Result<(), nu_protocol::ShellError> { use nu_plugin_test_support::PluginTest; - PluginTest::new("example", Example.into())?.test_command_examples(&CollectExternal) + PluginTest::new("example", ExamplePlugin.into())?.test_command_examples(&CollectExternal) } diff --git a/crates/nu_plugin_example/src/commands/config.rs b/crates/nu_plugin_example/src/commands/config.rs index 7e63d82710..905f69dfec 100644 --- a/crates/nu_plugin_example/src/commands/config.rs +++ b/crates/nu_plugin_example/src/commands/config.rs @@ -1,25 +1,38 @@ use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; -use nu_protocol::{Category, LabeledError, PluginSignature, Type, Value}; +use nu_protocol::{Category, LabeledError, Signature, Type, Value}; -use crate::Example; +use crate::ExamplePlugin; pub struct Config; impl SimplePluginCommand for Config { - type Plugin = Example; + type Plugin = ExamplePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("example config") - .usage("Show plugin configuration") - .extra_usage("The configuration is set under $env.config.plugins.example") + fn name(&self) -> &str { + "example config" + } + + fn usage(&self) -> &str { + "Show plugin configuration" + } + + fn extra_usage(&self) -> &str { + "The configuration is set under $env.config.plugins.example" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .category(Category::Experimental) - .search_terms(vec!["example".into(), "configuration".into()]) .input_output_type(Type::Nothing, Type::Table(vec![])) } + fn search_terms(&self) -> Vec<&str> { + vec!["example", "configuration"] + } + fn run( &self, - _plugin: &Example, + _plugin: &ExamplePlugin, engine: &EngineInterface, call: &EvaluatedCall, _input: &Value, diff --git a/crates/nu_plugin_example/src/commands/disable_gc.rs b/crates/nu_plugin_example/src/commands/disable_gc.rs index 55ab256955..5ff7f508e0 100644 --- a/crates/nu_plugin_example/src/commands/disable_gc.rs +++ b/crates/nu_plugin_example/src/commands/disable_gc.rs @@ -1,18 +1,23 @@ use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; -use nu_protocol::{Category, LabeledError, PluginSignature, Value}; +use nu_protocol::{Category, LabeledError, Signature, Value}; -use crate::Example; +use crate::ExamplePlugin; pub struct DisableGc; impl SimplePluginCommand for DisableGc { - type Plugin = Example; + type Plugin = ExamplePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("example disable-gc") - .usage("Disable the plugin garbage collector for `example`") - .extra_usage( - "\ + fn name(&self) -> &str { + "example disable-gc" + } + + fn usage(&self) -> &str { + "Disable the plugin garbage collector for `example`" + } + + fn extra_usage(&self) -> &str { + "\ Plugins are garbage collected by default after a period of inactivity. This behavior is configurable with `$env.config.plugin_gc.default`, or to change it specifically for the example plugin, use @@ -20,21 +25,22 @@ specifically for the example plugin, use This command demonstrates how plugins can control this behavior and disable GC temporarily if they need to. It is still possible to stop the plugin explicitly -using `plugin stop example`.", - ) - .search_terms(vec![ - "example".into(), - "gc".into(), - "plugin_gc".into(), - "garbage".into(), - ]) +using `plugin stop example`." + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .switch("reset", "Turn the garbage collector back on", None) .category(Category::Experimental) } + fn search_terms(&self) -> Vec<&str> { + vec!["example", "gc", "plugin_gc", "garbage"] + } + fn run( &self, - _plugin: &Example, + _plugin: &ExamplePlugin, engine: &EngineInterface, call: &EvaluatedCall, _input: &Value, diff --git a/crates/nu_plugin_example/src/commands/env.rs b/crates/nu_plugin_example/src/commands/env.rs index c84934698b..66d37e84fa 100644 --- a/crates/nu_plugin_example/src/commands/env.rs +++ b/crates/nu_plugin_example/src/commands/env.rs @@ -1,17 +1,27 @@ use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; -use nu_protocol::{Category, LabeledError, PluginSignature, SyntaxShape, Type, Value}; +use nu_protocol::{Category, LabeledError, Signature, SyntaxShape, Type, Value}; -use crate::Example; +use crate::ExamplePlugin; pub struct Env; impl SimplePluginCommand for Env { - type Plugin = Example; + type Plugin = ExamplePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("example env") - .usage("Get environment variable(s)") - .extra_usage("Returns all environment variables if no name provided") + fn name(&self) -> &str { + "example env" + } + + fn usage(&self) -> &str { + "Get environment variable(s)" + } + + fn extra_usage(&self) -> &str { + "Returns all environment variables if no name provided" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .category(Category::Experimental) .optional( "name", @@ -25,13 +35,16 @@ impl SimplePluginCommand for Env { "Set an environment variable to the value", None, ) - .search_terms(vec!["example".into(), "env".into()]) .input_output_type(Type::Nothing, Type::Any) } + fn search_terms(&self) -> Vec<&str> { + vec!["example", "env"] + } + fn run( &self, - _plugin: &Example, + _plugin: &ExamplePlugin, engine: &EngineInterface, call: &EvaluatedCall, _input: &Value, diff --git a/crates/nu_plugin_example/src/commands/for_each.rs b/crates/nu_plugin_example/src/commands/for_each.rs index 3395ab02ce..b784deb88c 100644 --- a/crates/nu_plugin_example/src/commands/for_each.rs +++ b/crates/nu_plugin_example/src/commands/for_each.rs @@ -1,37 +1,48 @@ use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand}; -use nu_protocol::{ - Category, LabeledError, PipelineData, PluginExample, PluginSignature, SyntaxShape, Type, -}; +use nu_protocol::{Category, Example, LabeledError, PipelineData, Signature, SyntaxShape, Type}; -use crate::Example; +use crate::ExamplePlugin; /// ` | example for-each { |value| ... }` pub struct ForEach; impl PluginCommand for ForEach { - type Plugin = Example; + type Plugin = ExamplePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("example for-each") - .usage("Example execution of a closure with a stream") - .extra_usage("Prints each value the closure returns to stderr") + fn name(&self) -> &str { + "example for-each" + } + + fn usage(&self) -> &str { + "Example execution of a closure with a stream" + } + + fn extra_usage(&self) -> &str { + "Prints each value the closure returns to stderr" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .input_output_type(Type::ListStream, Type::Nothing) .required( "closure", SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), "The closure to run for each input value", ) - .plugin_examples(vec![PluginExample { - example: "ls | get name | example for-each { |f| ^file $f }".into(), - description: "example with an external command".into(), - result: None, - }]) .category(Category::Experimental) } + fn examples(&self) -> Vec { + vec![Example { + example: "ls | get name | example for-each { |f| ^file $f }", + description: "example with an external command", + result: None, + }] + } + fn run( &self, - _plugin: &Example, + _plugin: &ExamplePlugin, engine: &EngineInterface, call: &EvaluatedCall, input: PipelineData, @@ -49,5 +60,5 @@ impl PluginCommand for ForEach { #[test] fn test_examples() -> Result<(), nu_protocol::ShellError> { use nu_plugin_test_support::PluginTest; - PluginTest::new("example", Example.into())?.test_command_examples(&ForEach) + PluginTest::new("example", ExamplePlugin.into())?.test_command_examples(&ForEach) } diff --git a/crates/nu_plugin_example/src/commands/generate.rs b/crates/nu_plugin_example/src/commands/generate.rs index 011311ce9a..67d30dcb9c 100644 --- a/crates/nu_plugin_example/src/commands/generate.rs +++ b/crates/nu_plugin_example/src/commands/generate.rs @@ -1,21 +1,31 @@ use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand}; use nu_protocol::{ - Category, IntoInterruptiblePipelineData, LabeledError, PipelineData, PluginExample, - PluginSignature, SyntaxShape, Type, Value, + Category, Example, IntoInterruptiblePipelineData, LabeledError, PipelineData, Signature, + SyntaxShape, Type, Value, }; -use crate::Example; +use crate::ExamplePlugin; /// `example generate { |previous| {out: ..., next: ...} }` pub struct Generate; impl PluginCommand for Generate { - type Plugin = Example; + type Plugin = ExamplePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("example generate") - .usage("Example execution of a closure to produce a stream") - .extra_usage("See the builtin `generate` command") + fn name(&self) -> &str { + "example generate" + } + + fn usage(&self) -> &str { + "Example execution of a closure to produce a stream" + } + + fn extra_usage(&self) -> &str { + "See the builtin `generate` command" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .input_output_type(Type::Nothing, Type::ListStream) .required( "initial", @@ -27,23 +37,25 @@ impl PluginCommand for Generate { SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), "The closure to run to generate values", ) - .plugin_examples(vec![PluginExample { - example: "example generate 0 { |i| if $i <= 10 { {out: $i, next: ($i + 2)} } }" - .into(), - description: "Generate a sequence of numbers".into(), - result: Some(Value::test_list( - [0, 2, 4, 6, 8, 10] - .into_iter() - .map(Value::test_int) - .collect(), - )), - }]) .category(Category::Experimental) } + fn examples(&self) -> Vec { + vec![Example { + example: "example generate 0 { |i| if $i <= 10 { {out: $i, next: ($i + 2)} } }", + description: "Generate a sequence of numbers", + result: Some(Value::test_list( + [0, 2, 4, 6, 8, 10] + .into_iter() + .map(Value::test_int) + .collect(), + )), + }] + } + fn run( &self, - _plugin: &Example, + _plugin: &ExamplePlugin, engine: &EngineInterface, call: &EvaluatedCall, _input: PipelineData, @@ -81,7 +93,7 @@ impl PluginCommand for Generate { fn test_examples() -> Result<(), nu_protocol::ShellError> { use nu_cmd_lang::If; use nu_plugin_test_support::PluginTest; - PluginTest::new("example", Example.into())? + PluginTest::new("example", ExamplePlugin.into())? .add_decl(Box::new(If))? .test_command_examples(&Generate) } diff --git a/crates/nu_plugin_example/src/commands/main.rs b/crates/nu_plugin_example/src/commands/main.rs index 3dfb137fff..adc8000aeb 100644 --- a/crates/nu_plugin_example/src/commands/main.rs +++ b/crates/nu_plugin_example/src/commands/main.rs @@ -1,28 +1,38 @@ use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; -use nu_protocol::{Category, LabeledError, PluginSignature, Value}; +use nu_protocol::{Category, LabeledError, Signature, Value}; -use crate::Example; +use crate::ExamplePlugin; pub struct Main; impl SimplePluginCommand for Main { - type Plugin = Example; + type Plugin = ExamplePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("example") - .usage("Example commands for Nushell plugins") - .extra_usage( - r#" + fn name(&self) -> &str { + "example" + } + + fn usage(&self) -> &str { + "Example commands for Nushell plugins" + } + + fn extra_usage(&self) -> &str { + r#" The `example` plugin demonstrates usage of the Nushell plugin API. Several commands provided to test and demonstrate different capabilities of plugins exposed through the API. None of these commands are intended to be particularly useful. "# - .trim(), - ) - .search_terms(vec!["example".into()]) - .category(Category::Experimental) + .trim() + } + + fn signature(&self) -> Signature { + Signature::build(self.name()).category(Category::Experimental) + } + + fn search_terms(&self) -> Vec<&str> { + vec!["example"] } fn run( diff --git a/crates/nu_plugin_example/src/commands/one.rs b/crates/nu_plugin_example/src/commands/one.rs index dcf5951f67..e0824e3fcf 100644 --- a/crates/nu_plugin_example/src/commands/one.rs +++ b/crates/nu_plugin_example/src/commands/one.rs @@ -1,37 +1,53 @@ use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; -use nu_protocol::{Category, LabeledError, PluginExample, PluginSignature, SyntaxShape, Value}; +use nu_protocol::{Category, Example, LabeledError, Signature, SyntaxShape, Value}; -use crate::Example; +use crate::ExamplePlugin; pub struct One; impl SimplePluginCommand for One { - type Plugin = Example; + type Plugin = ExamplePlugin; - fn signature(&self) -> PluginSignature { + fn name(&self) -> &str { + "example one" + } + + fn usage(&self) -> &str { + "Plugin test example 1. Returns Value::Nothing" + } + + fn extra_usage(&self) -> &str { + "Extra usage for example one" + } + + fn signature(&self) -> Signature { // The signature defines the usage of the command inside Nu, and also automatically // generates its help page. - PluginSignature::build("example one") - .usage("PluginSignature test 1 for plugin. Returns Value::Nothing") - .extra_usage("Extra usage for example one") - .search_terms(vec!["example".into()]) + Signature::build(self.name()) .required("a", SyntaxShape::Int, "required integer value") .required("b", SyntaxShape::String, "required string value") .switch("flag", "a flag for the signature", Some('f')) .optional("opt", SyntaxShape::Int, "Optional number") .named("named", SyntaxShape::String, "named string", Some('n')) .rest("rest", SyntaxShape::String, "rest value string") - .plugin_examples(vec![PluginExample { - example: "example one 3 bb".into(), - description: "running example with an int value and string value".into(), - result: None, - }]) .category(Category::Experimental) } + fn search_terms(&self) -> Vec<&str> { + vec!["example"] + } + + fn examples(&self) -> Vec { + vec![Example { + example: "example one 3 bb", + description: "running example with an int value and string value", + result: None, + }] + } + fn run( &self, - plugin: &Example, + plugin: &ExamplePlugin, _engine: &EngineInterface, call: &EvaluatedCall, input: &Value, @@ -45,5 +61,5 @@ impl SimplePluginCommand for One { #[test] fn test_examples() -> Result<(), nu_protocol::ShellError> { use nu_plugin_test_support::PluginTest; - PluginTest::new("example", Example.into())?.test_command_examples(&One) + PluginTest::new("example", ExamplePlugin.into())?.test_command_examples(&One) } diff --git a/crates/nu_plugin_example/src/commands/seq.rs b/crates/nu_plugin_example/src/commands/seq.rs index db48a35ebc..52a21a646a 100644 --- a/crates/nu_plugin_example/src/commands/seq.rs +++ b/crates/nu_plugin_example/src/commands/seq.rs @@ -1,39 +1,51 @@ use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand}; use nu_protocol::{ - Category, LabeledError, ListStream, PipelineData, PluginExample, PluginSignature, SyntaxShape, - Type, Value, + Category, Example, LabeledError, ListStream, PipelineData, Signature, SyntaxShape, Type, Value, }; -use crate::Example; +use crate::ExamplePlugin; /// `example seq ` pub struct Seq; impl PluginCommand for Seq { - type Plugin = Example; + type Plugin = ExamplePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("example seq") - .usage("Example stream generator for a list of values") - .search_terms(vec!["example".into()]) + fn name(&self) -> &str { + "example seq" + } + + fn usage(&self) -> &str { + "Example stream generator for a list of values" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .required("first", SyntaxShape::Int, "first number to generate") .required("last", SyntaxShape::Int, "last number to generate") .input_output_type(Type::Nothing, Type::List(Type::Int.into())) - .plugin_examples(vec![PluginExample { - example: "example seq 1 3".into(), - description: "generate a sequence from 1 to 3".into(), - result: Some(Value::test_list(vec![ - Value::test_int(1), - Value::test_int(2), - Value::test_int(3), - ])), - }]) .category(Category::Experimental) } + fn search_terms(&self) -> Vec<&str> { + vec!["example"] + } + + fn examples(&self) -> Vec { + vec![Example { + example: "example seq 1 3", + description: "generate a sequence from 1 to 3", + result: Some(Value::test_list(vec![ + Value::test_int(1), + Value::test_int(2), + Value::test_int(3), + ])), + }] + } + fn run( &self, - _plugin: &Example, + _plugin: &ExamplePlugin, _engine: &EngineInterface, call: &EvaluatedCall, _input: PipelineData, @@ -50,5 +62,5 @@ impl PluginCommand for Seq { #[test] fn test_examples() -> Result<(), nu_protocol::ShellError> { use nu_plugin_test_support::PluginTest; - PluginTest::new("example", Example.into())?.test_command_examples(&Seq) + PluginTest::new("example", ExamplePlugin.into())?.test_command_examples(&Seq) } diff --git a/crates/nu_plugin_example/src/commands/sum.rs b/crates/nu_plugin_example/src/commands/sum.rs index 8a6b0601f0..6f748d426a 100644 --- a/crates/nu_plugin_example/src/commands/sum.rs +++ b/crates/nu_plugin_example/src/commands/sum.rs @@ -1,35 +1,46 @@ use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand}; -use nu_protocol::{ - Category, LabeledError, PipelineData, PluginExample, PluginSignature, Span, Type, Value, -}; +use nu_protocol::{Category, Example, LabeledError, PipelineData, Signature, Span, Type, Value}; -use crate::Example; +use crate::ExamplePlugin; /// ` | example sum` pub struct Sum; impl PluginCommand for Sum { - type Plugin = Example; + type Plugin = ExamplePlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("example sum") - .usage("Example stream consumer for a list of values") - .search_terms(vec!["example".into()]) + fn name(&self) -> &str { + "example sum" + } + + fn usage(&self) -> &str { + "Example stream consumer for a list of values" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .input_output_types(vec![ (Type::List(Type::Int.into()), Type::Int), (Type::List(Type::Float.into()), Type::Float), ]) - .plugin_examples(vec![PluginExample { - example: "example seq 1 5 | example sum".into(), - description: "sum values from 1 to 5".into(), - result: Some(Value::test_int(15)), - }]) .category(Category::Experimental) } + fn search_terms(&self) -> Vec<&str> { + vec!["example"] + } + + fn examples(&self) -> Vec { + vec![Example { + example: "example seq 1 5 | example sum", + description: "sum values from 1 to 5", + result: Some(Value::test_int(15)), + }] + } + fn run( &self, - _plugin: &Example, + _plugin: &ExamplePlugin, _engine: &EngineInterface, call: &EvaluatedCall, input: PipelineData, @@ -92,5 +103,5 @@ impl IntOrFloat { #[test] fn test_examples() -> Result<(), nu_protocol::ShellError> { use nu_plugin_test_support::PluginTest; - PluginTest::new("example", Example.into())?.test_command_examples(&Sum) + PluginTest::new("example", ExamplePlugin.into())?.test_command_examples(&Sum) } diff --git a/crates/nu_plugin_example/src/commands/three.rs b/crates/nu_plugin_example/src/commands/three.rs index 5c0c5192e1..c8e8554937 100644 --- a/crates/nu_plugin_example/src/commands/three.rs +++ b/crates/nu_plugin_example/src/commands/three.rs @@ -1,18 +1,25 @@ use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; -use nu_protocol::{Category, LabeledError, PluginSignature, SyntaxShape, Value}; +use nu_protocol::{Category, LabeledError, Signature, SyntaxShape, Value}; -use crate::Example; +use crate::ExamplePlugin; pub struct Three; impl SimplePluginCommand for Three { - type Plugin = Example; + type Plugin = ExamplePlugin; - fn signature(&self) -> PluginSignature { + fn name(&self) -> &str { + "example three" + } + + fn usage(&self) -> &str { + "Plugin test example 3. Returns labeled error" + } + + fn signature(&self) -> Signature { // The signature defines the usage of the command inside Nu, and also automatically // generates its help page. - PluginSignature::build("example three") - .usage("PluginSignature test 3 for plugin. Returns labeled error") + Signature::build(self.name()) .required("a", SyntaxShape::Int, "required integer value") .required("b", SyntaxShape::String, "required string value") .switch("flag", "a flag for the signature", Some('f')) @@ -24,7 +31,7 @@ impl SimplePluginCommand for Three { fn run( &self, - plugin: &Example, + plugin: &ExamplePlugin, _engine: &EngineInterface, call: &EvaluatedCall, input: &Value, diff --git a/crates/nu_plugin_example/src/commands/two.rs b/crates/nu_plugin_example/src/commands/two.rs index 5b60f62fc6..fcf2bf75ff 100644 --- a/crates/nu_plugin_example/src/commands/two.rs +++ b/crates/nu_plugin_example/src/commands/two.rs @@ -1,18 +1,25 @@ use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; -use nu_protocol::{record, Category, LabeledError, PluginSignature, SyntaxShape, Value}; +use nu_protocol::{record, Category, LabeledError, Signature, SyntaxShape, Value}; -use crate::Example; +use crate::ExamplePlugin; pub struct Two; impl SimplePluginCommand for Two { - type Plugin = Example; + type Plugin = ExamplePlugin; - fn signature(&self) -> PluginSignature { + fn name(&self) -> &str { + "example two" + } + + fn usage(&self) -> &str { + "Plugin test example 2. Returns list of records" + } + + fn signature(&self) -> Signature { // The signature defines the usage of the command inside Nu, and also automatically // generates its help page. - PluginSignature::build("example two") - .usage("PluginSignature test 2 for plugin. Returns list of records") + Signature::build(self.name()) .required("a", SyntaxShape::Int, "required integer value") .required("b", SyntaxShape::String, "required string value") .switch("flag", "a flag for the signature", Some('f')) @@ -24,7 +31,7 @@ impl SimplePluginCommand for Two { fn run( &self, - plugin: &Example, + plugin: &ExamplePlugin, _engine: &EngineInterface, call: &EvaluatedCall, input: &Value, diff --git a/crates/nu_plugin_example/src/example.rs b/crates/nu_plugin_example/src/example.rs index 19d23cc5da..38b7dcc30d 100644 --- a/crates/nu_plugin_example/src/example.rs +++ b/crates/nu_plugin_example/src/example.rs @@ -1,9 +1,9 @@ use nu_plugin::EvaluatedCall; use nu_protocol::{LabeledError, Value}; -pub struct Example; +pub struct ExamplePlugin; -impl Example { +impl ExamplePlugin { pub fn print_values( &self, index: u32, diff --git a/crates/nu_plugin_example/src/lib.rs b/crates/nu_plugin_example/src/lib.rs index f2af897286..1328473c1b 100644 --- a/crates/nu_plugin_example/src/lib.rs +++ b/crates/nu_plugin_example/src/lib.rs @@ -4,9 +4,9 @@ mod commands; mod example; pub use commands::*; -pub use example::Example; +pub use example::ExamplePlugin; -impl Plugin for Example { +impl Plugin for ExamplePlugin { fn commands(&self) -> Vec>> { // This is a list of all of the commands you would like Nu to register when your plugin is // loaded. diff --git a/crates/nu_plugin_example/src/main.rs b/crates/nu_plugin_example/src/main.rs index 1be1469316..27b6f8e672 100644 --- a/crates/nu_plugin_example/src/main.rs +++ b/crates/nu_plugin_example/src/main.rs @@ -1,12 +1,12 @@ use nu_plugin::{serve_plugin, MsgPackSerializer}; -use nu_plugin_example::Example; +use nu_plugin_example::ExamplePlugin; fn main() { // When defining your plugin, you can select the Serializer that could be // used to encode and decode the messages. The available options are // MsgPackSerializer and JsonSerializer. Both are defined in the serializer // folder in nu-plugin. - serve_plugin(&Example {}, MsgPackSerializer {}) + serve_plugin(&ExamplePlugin {}, MsgPackSerializer {}) // Note // When creating plugins in other languages one needs to consider how a plugin diff --git a/crates/nu_plugin_formats/src/from/eml.rs b/crates/nu_plugin_formats/src/from/eml.rs index d0485d16c1..5df336358a 100644 --- a/crates/nu_plugin_formats/src/from/eml.rs +++ b/crates/nu_plugin_formats/src/from/eml.rs @@ -4,20 +4,26 @@ use eml_parser::EmlParser; use indexmap::IndexMap; use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; use nu_protocol::{ - record, Category, LabeledError, PluginExample, PluginSignature, ShellError, Span, SyntaxShape, - Type, Value, + record, Category, Example, LabeledError, ShellError, Signature, Span, SyntaxShape, Type, Value, }; const DEFAULT_BODY_PREVIEW: usize = 50; -pub const CMD_NAME: &str = "from eml"; pub struct FromEml; impl SimplePluginCommand for FromEml { type Plugin = FromCmds; - fn signature(&self) -> nu_protocol::PluginSignature { - PluginSignature::build(CMD_NAME) + fn name(&self) -> &str { + "from eml" + } + + fn usage(&self) -> &str { + "Parse text as .eml and create record." + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .input_output_types(vec![(Type::String, Type::Record(vec![]))]) .named( "preview-body", @@ -25,11 +31,13 @@ impl SimplePluginCommand for FromEml { "How many bytes of the body to preview", Some('b'), ) - .usage("Parse text as .eml and create record.") - .plugin_examples(examples()) .category(Category::Formats) } + fn examples(&self) -> Vec { + examples() + } + fn run( &self, _plugin: &FromCmds, @@ -45,16 +53,15 @@ impl SimplePluginCommand for FromEml { } } -pub fn examples() -> Vec { +pub fn examples() -> Vec> { vec![ - PluginExample { - description: "Convert eml structured data into record".into(), + Example { + description: "Convert eml structured data into record", example: "'From: test@email.com Subject: Welcome To: someone@somewhere.com -Test' | from eml" - .into(), +Test' | from eml", result: Some(Value::test_record(record! { "Subject" => Value::test_string("Welcome"), "From" => Value::test_record(record! { @@ -68,14 +75,13 @@ Test' | from eml" "Body" => Value::test_string("Test"), })), }, - PluginExample { - description: "Convert eml structured data into record".into(), + Example { + description: "Convert eml structured data into record", example: "'From: test@email.com Subject: Welcome To: someone@somewhere.com -Test' | from eml -b 1" - .into(), +Test' | from eml -b 1", result: Some(Value::test_record(record! { "Subject" => Value::test_string("Welcome"), "From" => Value::test_record(record! { diff --git a/crates/nu_plugin_formats/src/from/ics.rs b/crates/nu_plugin_formats/src/from/ics.rs index 890d2d139c..34d43307b7 100644 --- a/crates/nu_plugin_formats/src/from/ics.rs +++ b/crates/nu_plugin_formats/src/from/ics.rs @@ -1,27 +1,36 @@ use crate::FromCmds; + use ical::{parser::ical::component::*, property::Property}; use indexmap::IndexMap; use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; use nu_protocol::{ - record, Category, LabeledError, PluginExample, PluginSignature, ShellError, Span, Type, Value, + record, Category, Example, LabeledError, ShellError, Signature, Span, Type, Value, }; use std::io::BufReader; -pub const CMD_NAME: &str = "from ics"; - pub struct FromIcs; impl SimplePluginCommand for FromIcs { type Plugin = FromCmds; - fn signature(&self) -> nu_protocol::PluginSignature { - PluginSignature::build(CMD_NAME) + fn name(&self) -> &str { + "from ics" + } + + fn usage(&self) -> &str { + "Parse text as .ics and create table." + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .input_output_types(vec![(Type::String, Type::Table(vec![]))]) - .usage("Parse text as .ics and create table.") - .plugin_examples(examples()) .category(Category::Formats) } + fn examples(&self) -> Vec { + examples() + } + fn run( &self, _plugin: &FromCmds, @@ -71,13 +80,11 @@ impl SimplePluginCommand for FromIcs { } } -pub fn examples() -> Vec { - vec![PluginExample { - example: " -'BEGIN:VCALENDAR -END:VCALENDAR' | from ics" - .into(), - description: "Converts ics formatted string to table".into(), +pub fn examples() -> Vec> { + vec![Example { + example: "'BEGIN:VCALENDAR +END:VCALENDAR' | from ics", + description: "Converts ics formatted string to table", result: Some(Value::test_list(vec![Value::test_record(record! { "properties" => Value::test_list(vec![]), "events" => Value::test_list(vec![]), diff --git a/crates/nu_plugin_formats/src/from/ini.rs b/crates/nu_plugin_formats/src/from/ini.rs index e54fd3ab80..b6a861dc1d 100644 --- a/crates/nu_plugin_formats/src/from/ini.rs +++ b/crates/nu_plugin_formats/src/from/ini.rs @@ -1,24 +1,33 @@ use crate::FromCmds; + use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; use nu_protocol::{ - record, Category, LabeledError, PluginExample, PluginSignature, Record, ShellError, Type, Value, + record, Category, Example, LabeledError, Record, ShellError, Signature, Type, Value, }; -pub const CMD_NAME: &str = "from ini"; - pub struct FromIni; impl SimplePluginCommand for FromIni { type Plugin = FromCmds; - fn signature(&self) -> PluginSignature { - PluginSignature::build(CMD_NAME) + fn name(&self) -> &str { + "from ini" + } + + fn usage(&self) -> &str { + "Parse text as .ini and create table." + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .input_output_types(vec![(Type::String, Type::Record(vec![]))]) - .usage("Parse text as .ini and create table.") - .plugin_examples(examples()) .category(Category::Formats) } + fn examples(&self) -> Vec { + examples() + } + fn run( &self, _plugin: &FromCmds, @@ -73,13 +82,12 @@ impl SimplePluginCommand for FromIni { } } -pub fn examples() -> Vec { - vec![PluginExample { +pub fn examples() -> Vec> { + vec![Example { example: "'[foo] a=1 -b=2' | from ini" - .into(), - description: "Converts ini formatted string to record".into(), +b=2' | from ini", + description: "Converts ini formatted string to record", result: Some(Value::test_record(record! { "foo" => Value::test_record(record! { "a" => Value::test_string("1"), diff --git a/crates/nu_plugin_formats/src/from/vcf.rs b/crates/nu_plugin_formats/src/from/vcf.rs index 4656bc63f6..04dc351961 100644 --- a/crates/nu_plugin_formats/src/from/vcf.rs +++ b/crates/nu_plugin_formats/src/from/vcf.rs @@ -1,26 +1,35 @@ use crate::FromCmds; + use ical::{parser::vcard::component::*, property::Property}; use indexmap::IndexMap; use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; use nu_protocol::{ - record, Category, LabeledError, PluginExample, PluginSignature, ShellError, Span, Type, Value, + record, Category, Example, LabeledError, ShellError, Signature, Span, Type, Value, }; -pub const CMD_NAME: &str = "from vcf"; - pub struct FromVcf; impl SimplePluginCommand for FromVcf { type Plugin = FromCmds; - fn signature(&self) -> PluginSignature { - PluginSignature::build(CMD_NAME) + fn name(&self) -> &str { + "from vcf" + } + + fn usage(&self) -> &str { + "Parse text as .vcf and create table." + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .input_output_types(vec![(Type::String, Type::Table(vec![]))]) - .usage("Parse text as .vcf and create table.") - .plugin_examples(examples()) .category(Category::Formats) } + fn examples(&self) -> Vec { + examples() + } + fn run( &self, _plugin: &FromCmds, @@ -68,15 +77,14 @@ impl SimplePluginCommand for FromVcf { } } -pub fn examples() -> Vec { - vec![PluginExample { +pub fn examples() -> Vec> { + vec![Example { example: "'BEGIN:VCARD N:Foo FN:Bar EMAIL:foo@bar.com -END:VCARD' | from vcf" - .into(), - description: "Converts ics formatted string to table".into(), +END:VCARD' | from vcf", + description: "Converts ics formatted string to table", result: Some(Value::test_list(vec![Value::test_record(record! { "properties" => Value::test_list( vec![ diff --git a/crates/nu_plugin_gstat/src/nu/mod.rs b/crates/nu_plugin_gstat/src/nu/mod.rs index c170e61870..223e5a49b5 100644 --- a/crates/nu_plugin_gstat/src/nu/mod.rs +++ b/crates/nu_plugin_gstat/src/nu/mod.rs @@ -1,6 +1,6 @@ use crate::GStat; use nu_plugin::{EngineInterface, EvaluatedCall, Plugin, PluginCommand, SimplePluginCommand}; -use nu_protocol::{Category, LabeledError, PluginSignature, Spanned, SyntaxShape, Value}; +use nu_protocol::{Category, LabeledError, Signature, Spanned, SyntaxShape, Value}; pub struct GStatPlugin; @@ -13,9 +13,16 @@ impl Plugin for GStatPlugin { impl SimplePluginCommand for GStat { type Plugin = GStatPlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("gstat") - .usage("Get the git status of a repo") + fn name(&self) -> &str { + "gstat" + } + + fn usage(&self) -> &str { + "Get the git status of a repo" + } + + fn signature(&self) -> Signature { + Signature::build(PluginCommand::name(self)) .optional("path", SyntaxShape::Filepath, "path to repo") .category(Category::Custom("prompt".to_string())) } diff --git a/crates/nu_plugin_inc/src/nu/mod.rs b/crates/nu_plugin_inc/src/nu/mod.rs index 0647d1187c..148f1a7002 100644 --- a/crates/nu_plugin_inc/src/nu/mod.rs +++ b/crates/nu_plugin_inc/src/nu/mod.rs @@ -1,6 +1,6 @@ use crate::{inc::SemVerAction, Inc}; use nu_plugin::{EngineInterface, EvaluatedCall, Plugin, PluginCommand, SimplePluginCommand}; -use nu_protocol::{ast::CellPath, LabeledError, PluginSignature, SyntaxShape, Value}; +use nu_protocol::{ast::CellPath, LabeledError, Signature, SyntaxShape, Value}; pub struct IncPlugin; @@ -13,9 +13,16 @@ impl Plugin for IncPlugin { impl SimplePluginCommand for Inc { type Plugin = IncPlugin; - fn signature(&self) -> PluginSignature { - PluginSignature::build("inc") - .usage("Increment a value or version. Optionally use the column of a table.") + fn name(&self) -> &str { + "inc" + } + + fn usage(&self) -> &str { + "Increment a value or version. Optionally use the column of a table." + } + + fn signature(&self) -> Signature { + Signature::build(PluginCommand::name(self)) .optional("cell_path", SyntaxShape::CellPath, "cell path to update") .switch( "major", diff --git a/crates/nu_plugin_query/src/query.rs b/crates/nu_plugin_query/src/query.rs index 5d76313ac2..1e143068c4 100644 --- a/crates/nu_plugin_query/src/query.rs +++ b/crates/nu_plugin_query/src/query.rs @@ -1,6 +1,6 @@ use crate::{query_json::QueryJson, query_web::QueryWeb, query_xml::QueryXml}; use nu_plugin::{EvaluatedCall, Plugin, PluginCommand, SimplePluginCommand}; -use nu_protocol::{Category, LabeledError, PluginSignature, Value}; +use nu_protocol::{Category, LabeledError, Signature, Value}; #[derive(Default)] pub struct Query; @@ -32,10 +32,16 @@ pub struct QueryCommand; impl SimplePluginCommand for QueryCommand { type Plugin = Query; - fn signature(&self) -> PluginSignature { - PluginSignature::build("query") - .usage("Show all the query commands") - .category(Category::Filters) + fn name(&self) -> &str { + "query" + } + + fn usage(&self) -> &str { + "Show all the query commands" + } + + fn signature(&self) -> Signature { + Signature::build(PluginCommand::name(self)).category(Category::Filters) } fn run( diff --git a/crates/nu_plugin_query/src/query_json.rs b/crates/nu_plugin_query/src/query_json.rs index 5b6aeea03c..ee8b919d12 100644 --- a/crates/nu_plugin_query/src/query_json.rs +++ b/crates/nu_plugin_query/src/query_json.rs @@ -1,20 +1,23 @@ use crate::Query; use gjson::Value as gjValue; use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; -use nu_protocol::{ - Category, LabeledError, PluginSignature, Record, Span, Spanned, SyntaxShape, Value, -}; +use nu_protocol::{Category, LabeledError, Record, Signature, Span, Spanned, SyntaxShape, Value}; pub struct QueryJson; impl SimplePluginCommand for QueryJson { type Plugin = Query; - fn signature(&self) -> PluginSignature { - PluginSignature::build("query json") - .usage( - "execute json query on json file (open --raw | query json 'query string')", - ) + fn name(&self) -> &str { + "query json" + } + + fn usage(&self) -> &str { + "execute json query on json file (open --raw | query json 'query string')" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .required("query", SyntaxShape::String, "json query") .category(Category::Filters) } diff --git a/crates/nu_plugin_query/src/query_web.rs b/crates/nu_plugin_query/src/query_web.rs index b4e59b681b..2c27fce8e2 100644 --- a/crates/nu_plugin_query/src/query_web.rs +++ b/crates/nu_plugin_query/src/query_web.rs @@ -1,8 +1,7 @@ use crate::{web_tables::WebTable, Query}; use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; use nu_protocol::{ - Category, LabeledError, PluginExample, PluginSignature, Record, Span, Spanned, SyntaxShape, - Value, + Category, Example, LabeledError, Record, Signature, Span, Spanned, SyntaxShape, Value, }; use scraper::{Html, Selector as ScraperSelector}; @@ -11,12 +10,18 @@ pub struct QueryWeb; impl SimplePluginCommand for QueryWeb { type Plugin = Query; - fn signature(&self) -> PluginSignature { - PluginSignature::build("query web") - .usage("execute selector query on html/web") + fn name(&self) -> &str { + "query web" + } + + fn usage(&self) -> &str { + "execute selector query on html/web" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .named("query", SyntaxShape::String, "selector query", Some('q')) .switch("as-html", "return the query output as html", Some('m')) - .plugin_examples(web_examples()) .named( "attribute", SyntaxShape::String, @@ -37,6 +42,10 @@ impl SimplePluginCommand for QueryWeb { .category(Category::Network) } + fn examples(&self) -> Vec { + web_examples() + } + fn run( &self, _plugin: &Query, @@ -48,27 +57,27 @@ impl SimplePluginCommand for QueryWeb { } } -pub fn web_examples() -> Vec { +pub fn web_examples() -> Vec> { vec![ - PluginExample { - example: "http get https://phoronix.com | query web --query 'header' | flatten".into(), - description: "Retrieve all `
` elements from phoronix.com website".into(), + Example { + example: "http get https://phoronix.com | query web --query 'header' | flatten", + description: "Retrieve all `
` elements from phoronix.com website", result: None, }, - PluginExample { + Example { example: "http get https://en.wikipedia.org/wiki/List_of_cities_in_India_by_population | - query web --as-table [City 'Population(2011)[3]' 'Population(2001)[3][a]' 'State or unionterritory' 'Ref']".into(), - description: "Retrieve a html table from Wikipedia and parse it into a nushell table using table headers as guides".into(), + query web --as-table [City 'Population(2011)[3]' 'Population(2001)[3][a]' 'State or unionterritory' 'Ref']", + description: "Retrieve a html table from Wikipedia and parse it into a nushell table using table headers as guides", result: None }, - PluginExample { - example: "http get https://www.nushell.sh | query web --query 'h2, h2 + p' | each {str join} | group 2 | each {rotate --ccw tagline description} | flatten".into(), - description: "Pass multiple css selectors to extract several elements within single query, group the query results together and rotate them to create a table".into(), + Example { + example: "http get https://www.nushell.sh | query web --query 'h2, h2 + p' | each {str join} | group 2 | each {rotate --ccw tagline description} | flatten", + description: "Pass multiple css selectors to extract several elements within single query, group the query results together and rotate them to create a table", result: None, }, - PluginExample { - example: "http get https://example.org | query web --query a --attribute href".into(), - description: "Retrieve a specific html attribute instead of the default text".into(), + Example { + example: "http get https://example.org | query web --query a --attribute href", + description: "Retrieve a specific html attribute instead of the default text", result: None, } ] diff --git a/crates/nu_plugin_query/src/query_xml.rs b/crates/nu_plugin_query/src/query_xml.rs index 1c1c527ad4..3cbd9d6092 100644 --- a/crates/nu_plugin_query/src/query_xml.rs +++ b/crates/nu_plugin_query/src/query_xml.rs @@ -1,7 +1,7 @@ use crate::Query; use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand}; use nu_protocol::{ - record, Category, LabeledError, PluginSignature, Record, Span, Spanned, SyntaxShape, Value, + record, Category, LabeledError, Record, Signature, Span, Spanned, SyntaxShape, Value, }; use sxd_document::parser; use sxd_xpath::{Context, Factory}; @@ -11,9 +11,16 @@ pub struct QueryXml; impl SimplePluginCommand for QueryXml { type Plugin = Query; - fn signature(&self) -> PluginSignature { - PluginSignature::build("query xml") - .usage("execute xpath query on xml") + fn name(&self) -> &str { + "query xml" + } + + fn usage(&self) -> &str { + "execute xpath query on xml" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) .required("query", SyntaxShape::String, "xpath query") .category(Category::Filters) } diff --git a/tests/plugins/register.rs b/tests/plugins/register.rs index 7646ae6f23..7ce641fec0 100644 --- a/tests/plugins/register.rs +++ b/tests/plugins/register.rs @@ -10,7 +10,7 @@ fn help() { "example one --help" ); - assert!(actual.out.contains("PluginSignature test 1")); + assert!(actual.out.contains("test example 1")); assert!(actual.out.contains("Extra usage for example one")); }) }