mirror of
https://github.com/nushell/nushell.git
synced 2025-05-05 15:32:56 +00:00
# Description This PR removes the `register` command which has been [deprecated](https://www.nushell.sh/blog/2024-04-30-nushell_0_93_0.html#register-toc) in favor of [`plugin add`](https://www.nushell.sh/blog/2024-04-30-nushell_0_93_0.html#redesigned-plugin-management-commands-toc) # User-Facing Changes `register` is no longer available
397 lines
14 KiB
Rust
397 lines
14 KiB
Rust
use nu_protocol::{
|
|
Example, IntoSpanned, LabeledError, PipelineData, PluginExample, PluginSignature, ShellError,
|
|
Signature, Value,
|
|
};
|
|
|
|
use crate::{EngineInterface, EvaluatedCall, Plugin};
|
|
|
|
/// The API for a Nushell plugin command
|
|
///
|
|
/// This is the trait that Nushell plugin commands must implement. The methods defined on
|
|
/// `PluginCommand` are invoked by [`serve_plugin`](crate::serve_plugin) during plugin registration
|
|
/// and execution.
|
|
///
|
|
/// The plugin command must be able to be safely shared between threads, so that multiple
|
|
/// invocations can be run in parallel. If interior mutability is desired, consider synchronization
|
|
/// primitives such as [mutexes](std::sync::Mutex) and [channels](std::sync::mpsc).
|
|
///
|
|
/// This version of the trait expects stream input and output. If you have a simple plugin that just
|
|
/// operates on plain values, consider using [`SimplePluginCommand`] instead.
|
|
///
|
|
/// # Examples
|
|
/// Basic usage:
|
|
/// ```
|
|
/// # use nu_plugin::*;
|
|
/// # use nu_protocol::{Signature, PipelineData, Type, Value, LabeledError};
|
|
/// struct LowercasePlugin;
|
|
/// struct Lowercase;
|
|
///
|
|
/// impl PluginCommand for Lowercase {
|
|
/// type Plugin = LowercasePlugin;
|
|
///
|
|
/// 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()))
|
|
/// }
|
|
///
|
|
/// fn run(
|
|
/// &self,
|
|
/// 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)?)
|
|
/// }
|
|
/// }
|
|
///
|
|
/// # impl Plugin for LowercasePlugin {
|
|
/// # fn version(&self) -> String {
|
|
/// # "0.0.0".into()
|
|
/// # }
|
|
/// # fn commands(&self) -> Vec<Box<dyn PluginCommand<Plugin=Self>>> {
|
|
/// # vec![Box::new(Lowercase)]
|
|
/// # }
|
|
/// # }
|
|
/// #
|
|
/// # fn main() {
|
|
/// # serve_plugin(&LowercasePlugin{}, MsgPackSerializer)
|
|
/// # }
|
|
/// ```
|
|
pub trait PluginCommand: Sync {
|
|
/// 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 name of the command from within Nu.
|
|
///
|
|
/// In case this contains spaces, it will be treated as a subcommand.
|
|
fn name(&self) -> &str;
|
|
|
|
/// 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
|
|
/// serialized returned value or error to stdout, which Nushell will interpret.
|
|
///
|
|
/// `engine` provides an interface back to the Nushell engine. See [`EngineInterface`] docs for
|
|
/// details on what methods are available.
|
|
///
|
|
/// The `call` contains metadata describing how the plugin command was invoked, including
|
|
/// arguments, and `input` contains the structured data piped into the command.
|
|
///
|
|
/// This variant expects to receive and produce [`PipelineData`], which allows for stream-based
|
|
/// handling of I/O. This is recommended if the plugin is expected to transform large
|
|
/// lists or potentially large quantities of bytes. The API is more complex however, and
|
|
/// [`SimplePluginCommand`] is recommended instead if this is not a concern.
|
|
fn run(
|
|
&self,
|
|
plugin: &Self::Plugin,
|
|
engine: &EngineInterface,
|
|
call: &EvaluatedCall,
|
|
input: PipelineData,
|
|
) -> Result<PipelineData, LabeledError>;
|
|
}
|
|
|
|
/// The API for a simple Nushell plugin command
|
|
///
|
|
/// This trait is an alternative to [`PluginCommand`], and operates on values instead of streams.
|
|
/// Note that this may make handling large lists more difficult.
|
|
///
|
|
/// The plugin command must be able to be safely shared between threads, so that multiple
|
|
/// invocations can be run in parallel. If interior mutability is desired, consider synchronization
|
|
/// primitives such as [mutexes](std::sync::Mutex) and [channels](std::sync::mpsc).
|
|
///
|
|
/// # Examples
|
|
/// Basic usage:
|
|
/// ```
|
|
/// # use nu_plugin::*;
|
|
/// # use nu_protocol::{LabeledError, Signature, Type, Value};
|
|
/// struct HelloPlugin;
|
|
/// struct Hello;
|
|
///
|
|
/// impl SimplePluginCommand for Hello {
|
|
/// type Plugin = HelloPlugin;
|
|
///
|
|
/// 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)
|
|
/// }
|
|
///
|
|
/// fn run(
|
|
/// &self,
|
|
/// plugin: &HelloPlugin,
|
|
/// engine: &EngineInterface,
|
|
/// call: &EvaluatedCall,
|
|
/// input: &Value,
|
|
/// ) -> Result<Value, LabeledError> {
|
|
/// Ok(Value::string("Hello, World!".to_owned(), call.head))
|
|
/// }
|
|
/// }
|
|
///
|
|
/// # impl Plugin for HelloPlugin {
|
|
/// # fn version(&self) -> String {
|
|
/// # "0.0.0".into()
|
|
/// # }
|
|
/// # fn commands(&self) -> Vec<Box<dyn PluginCommand<Plugin=Self>>> {
|
|
/// # vec![Box::new(Hello)]
|
|
/// # }
|
|
/// # }
|
|
/// #
|
|
/// # fn main() {
|
|
/// # serve_plugin(&HelloPlugin{}, MsgPackSerializer)
|
|
/// # }
|
|
/// ```
|
|
pub trait SimplePluginCommand: Sync {
|
|
/// 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 name of the command from within Nu.
|
|
///
|
|
/// In case this contains spaces, it will be treated as a subcommand.
|
|
fn name(&self) -> &str;
|
|
|
|
/// 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
|
|
/// serialized returned value or error to stdout, which Nushell will interpret.
|
|
///
|
|
/// `engine` provides an interface back to the Nushell engine. See [`EngineInterface`] docs for
|
|
/// details on what methods are available.
|
|
///
|
|
/// The `call` contains metadata describing how the plugin command was invoked, including
|
|
/// arguments, and `input` contains the structured data piped into the command.
|
|
///
|
|
/// This variant does not support streaming. Consider implementing [`PluginCommand`] directly
|
|
/// if streaming is desired.
|
|
fn run(
|
|
&self,
|
|
plugin: &Self::Plugin,
|
|
engine: &EngineInterface,
|
|
call: &EvaluatedCall,
|
|
input: &Value,
|
|
) -> Result<Value, LabeledError>;
|
|
}
|
|
|
|
/// All [`SimplePluginCommand`]s can be used as [`PluginCommand`]s, but input streams will be fully
|
|
/// consumed before the plugin command runs.
|
|
impl<T> PluginCommand for T
|
|
where
|
|
T: SimplePluginCommand,
|
|
{
|
|
type Plugin = <Self as SimplePluginCommand>::Plugin;
|
|
|
|
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(
|
|
&self,
|
|
plugin: &Self::Plugin,
|
|
engine: &EngineInterface,
|
|
call: &EvaluatedCall,
|
|
input: PipelineData,
|
|
) -> Result<PipelineData, LabeledError> {
|
|
// Unwrap the PipelineData from input, consuming the potential stream, and pass it to the
|
|
// simpler signature in Plugin
|
|
let span = input.span().unwrap_or(call.head);
|
|
let input_value = input.into_value(span)?;
|
|
// Wrap the output in PipelineData::Value
|
|
<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 `plugin add`.
|
|
///
|
|
/// 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(),
|
|
)
|
|
}
|
|
|
|
/// Render examples to their base value so they can be sent in the response to `Signature`.
|
|
pub(crate) fn render_examples(
|
|
plugin: &impl Plugin,
|
|
engine: &EngineInterface,
|
|
examples: &mut [PluginExample],
|
|
) -> Result<(), ShellError> {
|
|
for example in examples {
|
|
if let Some(ref mut value) = example.result {
|
|
value.recurse_mut(&mut |value| {
|
|
let span = value.span();
|
|
match value {
|
|
Value::Custom { .. } => {
|
|
let value_taken = std::mem::replace(value, Value::nothing(span));
|
|
let Value::Custom { val, .. } = value_taken else {
|
|
unreachable!()
|
|
};
|
|
*value =
|
|
plugin.custom_value_to_base_value(engine, val.into_spanned(span))?;
|
|
Ok::<_, ShellError>(())
|
|
}
|
|
_ => Ok(()),
|
|
}
|
|
})?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|