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<nu_protocol::Example>`
- `PluginSignature` and `PluginExample` no longer need to be used by
plugin developers

# User-Facing Changes
Breaking API for plugins yet again 😄
This commit is contained in:
Devyn Cairns 2024-03-27 03:59:57 -07:00 committed by GitHub
parent 03b5e9d853
commit 01d30a416b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 962 additions and 674 deletions

View File

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

View File

@ -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<Example> {
//! 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<PipelineData, LabeledError> {
//! let span = call.head;
//! Ok(input.map(move |value| {
//! value.as_str()
//! 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)?)
//! },
//! 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();
//! ```

View File

@ -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())
}
}

View File

@ -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(),
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<Example> {
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(

View File

@ -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(),
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<Example> {
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());

View File

@ -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(
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()),
)
.plugin_examples(vec![PluginExample {
example: r#"[Hello wORLD] | lowercase"#.into(),
description: "Lowercase a list of strings".into(),
}
fn examples(&self) -> Vec<Example> {
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(

View File

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

View File

@ -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<Example> {
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<Example> {
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 = <Self as SimplePluginCommand>::Plugin;
fn signature(&self) -> PluginSignature {
<Self as SimplePluginCommand>::signature(self)
fn examples(&self) -> Vec<Example> {
<Self as SimplePluginCommand>::examples(self)
}
fn extra_usage(&self) -> &str {
<Self as SimplePluginCommand>::extra_usage(self)
}
fn name(&self) -> &str {
<Self as SimplePluginCommand>::name(self)
}
fn run(
@ -204,4 +317,45 @@ where
<Self as SimplePluginCommand>::run(self, plugin, engine, call, &input_value)
.map(|value| PipelineData::Value(value, None))
}
fn search_terms(&self) -> Vec<&str> {
<Self as SimplePluginCommand>::search_terms(self)
}
fn signature(&self) -> Signature {
<Self as SimplePluginCommand>::signature(self)
}
fn usage(&self) -> &str {
<Self as SimplePluginCommand>::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(),
)
}

View File

@ -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");

View File

@ -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<String, _> = 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,
" ...{} <{}>: {}",

View File

@ -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,7 +213,8 @@ macro_rules! generate_tests {
#[test]
fn response_round_trip_signature() {
let signature = PluginSignature::build("nu-plugin")
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'))
@ -221,7 +224,9 @@ macro_rules! generate_tests {
"second named",
Some('s'),
)
.rest("remaining", SyntaxShape::Int, "remaining");
.rest("remaining", SyntaxShape::Int, "remaining"),
vec![],
);
let response = PluginCallResponse::Signature(vec![signature.clone()]);
let output = PluginOutput::CallResponse(3, response);

View File

@ -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)

View File

@ -19,3 +19,13 @@ pub struct PluginExample {
pub description: String,
pub result: Option<Value>,
}
impl From<Example<'_>> for PluginExample {
fn from(value: Example) -> Self {
PluginExample {
example: value.example.into(),
description: value.description.into(),
result: value.result,
}
}
}

View File

@ -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<String>) -> 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<String>) -> PluginSignature {
self.sig = self.sig.usage(msg);
self
}
/// Add an extra description to the signature
pub fn extra_usage(mut self, msg: impl Into<String>) -> PluginSignature {
self.sig = self.sig.extra_usage(msg);
self
}
/// Add search terms to the signature
pub fn search_terms(mut self, terms: Vec<String>) -> 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<String>,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
) -> 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<String>,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
) -> PluginSignature {
self.sig = self.sig.optional(name, shape, desc);
self
}
pub fn rest(
mut self,
name: &str,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
) -> 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<String>,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
short: Option<char>,
) -> 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<String>,
shape: impl Into<SyntaxShape>,
desc: impl Into<String>,
short: Option<char>,
) -> 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<String>,
desc: impl Into<String>,
short: Option<char>,
) -> 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<char> {
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<PositionalArg> {
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<Flag> {
self.sig.get_long_flag(name)
}
/// Find the matching long flag
pub fn get_short_flag(&self, short: char) -> Option<Flag> {
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<dyn Command> {
self.sig.predeclare()
}
/// Combines a signature and a block into a runnable block
pub fn into_block_command(self, block_id: BlockId) -> Box<dyn Command> {
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<PluginExample>) -> PluginSignature {
self.examples = examples;
self
}
}

View File

@ -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)
}

View File

@ -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(),
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<Example> {
vec![Example {
example: "custom-value generate",
description: "Generate a new CoolCustomValue",
result: Some(CoolCustomValue::new("abc").into_value(Span::test_data())),
}])
}]
}
fn run(

View File

@ -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(),
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
example: "custom-value generate2",
description: "Generate a new SecondCustomValue",
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(),
Example {
example: "custom-value generate2 { print }",
description: "Generate a new SecondCustomValue and pass it to a closure",
result: None,
},
])
]
}
fn run(

View File

@ -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(),
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<Example> {
vec![
Example {
example: "custom-value generate | custom-value update",
description: "Update a CoolCustomValue",
result: Some(CoolCustomValue::new("abcxyz").into_value(Span::test_data())),
},
PluginExample {
example: "custom-value generate2 | custom-value update".into(),
description: "Update a SecondCustomValue".into(),
Example {
example: "custom-value generate2 | custom-value update",
description: "Update a SecondCustomValue",
result: Some(SecondCustomValue::new("xyzabc").into_value(Span::test_data())),
},
])
]
}
fn run(

View File

@ -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,

View File

@ -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;
/// `<list<string>> | 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<Example> {
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)
}

View File

@ -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,

View File

@ -1,17 +1,22 @@
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
@ -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,

View File

@ -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,

View File

@ -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;
/// `<list> | 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<Example> {
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)
}

View File

@ -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 <initial> { |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(),
.category(Category::Experimental)
}
fn examples(&self) -> Vec<Example> {
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(),
)),
}])
.category(Category::Experimental)
}]
}
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)
}

View File

@ -1,17 +1,22 @@
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(
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.
@ -19,10 +24,15 @@ 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(

View File

@ -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<Example> {
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)
}

View File

@ -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 <first> <last>`
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(),
.category(Category::Experimental)
}
fn search_terms(&self) -> Vec<&str> {
vec!["example"]
}
fn examples(&self) -> Vec<Example> {
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),
])),
}])
.category(Category::Experimental)
}]
}
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)
}

View File

@ -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;
/// `<list> | 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<Example> {
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)
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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<Box<dyn PluginCommand<Plugin = Self>>> {
// This is a list of all of the commands you would like Nu to register when your plugin is
// loaded.

View File

@ -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

View File

@ -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<Example> {
examples()
}
fn run(
&self,
_plugin: &FromCmds,
@ -45,16 +53,15 @@ impl SimplePluginCommand for FromEml {
}
}
pub fn examples() -> Vec<PluginExample> {
pub fn examples() -> Vec<Example<'static>> {
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! {

View File

@ -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<Example> {
examples()
}
fn run(
&self,
_plugin: &FromCmds,
@ -71,13 +80,11 @@ impl SimplePluginCommand for FromIcs {
}
}
pub fn examples() -> Vec<PluginExample> {
vec![PluginExample {
example: "
'BEGIN:VCALENDAR
END:VCALENDAR' | from ics"
.into(),
description: "Converts ics formatted string to table".into(),
pub fn examples() -> Vec<Example<'static>> {
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![]),

View File

@ -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<Example> {
examples()
}
fn run(
&self,
_plugin: &FromCmds,
@ -73,13 +82,12 @@ impl SimplePluginCommand for FromIni {
}
}
pub fn examples() -> Vec<PluginExample> {
vec![PluginExample {
pub fn examples() -> Vec<Example<'static>> {
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"),

View File

@ -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<Example> {
examples()
}
fn run(
&self,
_plugin: &FromCmds,
@ -68,15 +77,14 @@ impl SimplePluginCommand for FromVcf {
}
}
pub fn examples() -> Vec<PluginExample> {
vec![PluginExample {
pub fn examples() -> Vec<Example<'static>> {
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![

View File

@ -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()))
}

View File

@ -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",

View File

@ -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(

View File

@ -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 <file> | query json 'query string')",
)
fn name(&self) -> &str {
"query json"
}
fn usage(&self) -> &str {
"execute json query on json file (open --raw <file> | query json 'query string')"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("query", SyntaxShape::String, "json query")
.category(Category::Filters)
}

View File

@ -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<Example> {
web_examples()
}
fn run(
&self,
_plugin: &Query,
@ -48,27 +57,27 @@ impl SimplePluginCommand for QueryWeb {
}
}
pub fn web_examples() -> Vec<PluginExample> {
pub fn web_examples() -> Vec<Example<'static>> {
vec![
PluginExample {
example: "http get https://phoronix.com | query web --query 'header' | flatten".into(),
description: "Retrieve all `<header>` elements from phoronix.com website".into(),
Example {
example: "http get https://phoronix.com | query web --query 'header' | flatten",
description: "Retrieve all `<header>` 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,
}
]

View File

@ -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)
}

View File

@ -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"));
})
}