Home Explore Blog CI



nushell

4th chunk of `contributor-book/plugins.md`
ff407473447b59e24396289b20dcd8c09e31d7c8dba893870000000100000fac
impl Plugin for LenPlugin {
    fn version(&self) -> String {
        env!("CARGO_PKG_VERSION").into()
    }

    fn commands(&self) -> Vec<Box<dyn PluginCommand<Plugin = Self>>> {
        vec![
            Box::new(Len),
        ]
    }
}
```

Again, we use a unit struct for `LenPlugin`, but this is the recommended place to put plugin state if needed. All commands also get a reference to the plugin type. This is what we eventually pass to `serve_plugin()` in `main()`.

`Plugin` has two required methods: `version()`, which reports the plugin's version back to Nu, and `commands()`, which initializes the plugin's commands. A boxed `dyn` reference is used so that we can keep all of the different command types in the single list. Dispatch by command name is automatically handled in `serve_plugin()` by looking at the name defined in the signature - in our case, that's `len`. A plugin can contain many commands, so if you end up adding more, just add them to the list returned by `commands()`.

For the version, we just use the `CARGO_PKG_VERSION` environment variable available at compile-time in order to get our plugin's version from Cargo.

Lastly, let's look at the top of the file:

```rust
use nu_plugin::{serve_plugin, JsonSerializer, EvaluatedCall};
use nu_plugin::{Plugin, PluginCommand, SimplePluginCommand, EngineInterface};
use nu_protocol::{LabeledError, Signature, Type, Value};
```

Here we import everything we need -- types and functions -- to be able to create our plugin.

Once we have finished our plugin, to use it all we need to do is install it.

```nu
> cargo install --path . --locked
# nushell only (run with `nu -c` if not in nushell)
> plugin add ~/.cargo/bin/nu_plugin_len # add .exe on Windows
```

If you're already running `nu` during the installation process of your plugin, ensure you restart `nu` so that it can load your plugin, or call `plugin use` to load it immediately:

```nu
> plugin use len # the name of the plugin (without `nu_plugin_`)
```

Once `nu` starts up, it will discover the plugin and add its commands to the scope.

```nu
nu
"hello" | len
# => 5
help len
# => calculates the length of its input
# => 
# => Usage:
# =>   > len
# => 
# => Flags:
# =>   -h, --help - Display the help message for this command
# => 
# => Signatures:
# =>   <string> | len -> <int>
```

Run `plugin list` to see all plugins currently registered and available to this Nu session, including whether or not they are running, and their process ID if so.

## Using streams in plugins

The `SimplePluginCommand` trait that we just implemented for our plugin does not support streaming input or output. If we want to extend our plugin to support determining the lengths of lists, it would be helpful to not have to consume an entire list that is a stream. We can do this by implementing `PluginCommand` instead.

```rust
// add these imports:
use nu_protocol::{IntoPipelineData, PipelineData};
// ...

// change SimplePluginCommand to PluginCommand:
impl PluginCommand for Len {
    type Plugin = LenPlugin;

    // ...

    fn signature(&self) -> Signature {
        // ... add the list type to the signature
        Signature::build(PluginCommand::name(self))
            .input_output_types(vec![
                (Type::String, Type::Int),
                (Type::List(Type::Any.into()), Type::Int),
            ])
    }

    // ... and change input and output types to PipelineData
    fn run(
        &self,
        _plugin: &LenPlugin,
        _engine: &EngineInterface,
        call: &EvaluatedCall,
        input: PipelineData,
    ) -> Result<PipelineData, LabeledError> {
        // Check if the input is a stream or list
        match input {
            PipelineData::ListStream(..) |
            PipelineData::Value(Value::List { .. }, _) => {
                // Count the iterable's elements
                let length = input.into_iter().count();
                Ok(
                    Value::int(length as i64, call.head).into_pipeline_data()
                )

Title: Plugin Implementation Details and Usage
Summary
This section details how the `version()` and `commands()` methods are implemented for the `LenPlugin`, and how `CARGO_PKG_VERSION` environment variable is used to get the plugin's version. Also listed are the necessary imports from `nu_plugin` and `nu_protocol` needed to create the plugin. It then covers the installation and usage of the plugin, including how to install it with Cargo, add it to Nu, and use the `len` command. Finally, it introduces how to use streams in plugins by implementing `PluginCommand` instead of `SimplePluginCommand` to handle list inputs without consuming the entire stream at once.