mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 00:13:21 +01:00
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:
parent
03b5e9d853
commit
01d30a416b
@ -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));
|
||||
}
|
||||
|
@ -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()
|
||||
//! .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();
|
||||
//! ```
|
||||
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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<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(
|
||||
|
@ -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<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());
|
||||
|
@ -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<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(
|
||||
|
@ -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};
|
||||
|
@ -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(),
|
||||
)
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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,
|
||||
" ...{} <{}>: {}",
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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<Example> {
|
||||
vec![Example {
|
||||
example: "custom-value generate",
|
||||
description: "Generate a new CoolCustomValue",
|
||||
result: Some(CoolCustomValue::new("abc").into_value(Span::test_data())),
|
||||
}]
|
||||
}
|
||||
|
||||
fn run(
|
||||
|
@ -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<Example> {
|
||||
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(
|
||||
|
@ -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<Example> {
|
||||
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(
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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(),
|
||||
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<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(),
|
||||
)),
|
||||
}]
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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(),
|
||||
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<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),
|
||||
])),
|
||||
}]
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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! {
|
||||
|
@ -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![]),
|
||||
|
@ -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"),
|
||||
|
@ -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![
|
||||
|
@ -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()))
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
]
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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"));
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user