Home Explore Blog CI



nushell

5th chunk of `contributor-book/plugins.md`
4fe0900c33c89c7bbc0787355716b83e6c82dc6eedb957340000000100000fbc
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()
                )
            },
            PipelineData::Value(Value::String { val, .. }, _) => {
                Ok(Value::int(val.len() as i64, call.head).into_pipeline_data())
            },
            _ => Err(
                LabeledError::new(
                    "Expected String or iterable input from pipeline",
                ).with_label(
                    format!(
                        "requires string or iterable input; got {}",
                        input.get_type(),
                    ),
                    call.head,
                )
            ),
        }
    }
}
```

With this change, we can pipe a list (even a long one) to the command to get its length:

```nu
$ seq 1 10000 | len
10000
```

Since `run()` also returns `PipelineData`, it is also possible for the plugin to produce a stream, or even to transform a stream. For example, if we wanted our plugin to multiply every integer by
two:

```rust
fn run(
    ..., 
    engine: &EngineInterface,
    call: &EvaluatedCall,
    input: PipelineData,
) -> Result<PipelineData, ShellError> {
    input.map(|value| {
        let span = value.span();
        match value.as_int() {
            Ok(int) => Value::int(int * 2, span),

            // In list streams (i.e., lists of `Value`), errors are always represented by
            // `Value::Error`.
            Err(err) => Value::error(err, span),
        }
    }, engine.signals()).map_err(|e|
        LabeledError::new(
            "Failed",
        ).with_label(
            format!(
                "Failed; {}",
                e,
            ),
            call.head,
        )
    )
}
```

Since the input and output are both streaming, this will work even on an infinite stream:

```nu
$ generate { |n| {out: $n, next: ($n + 1)} } 0 | plugin
0
2
4
6
8
# ...
```

## Plugin configuration

It is possible for a user to provide configuration to a plugin. For a plugin named `motd`:

```nu
$env.config.plugins = {
    motd: {
        message: "Nushell rocks!"
    }
}
```

The plugin configuration can be retrieved with [`EngineInterface::get_plugin_config`](https://docs.rs/nu-plugin/latest/nu_plugin/struct.EngineInterface.html#method.get_plugin_config).

```rust
use nu_plugin::*;
use nu_protocol::{Signature, Type, Value};

struct MotdPlugin;

impl Plugin for MotdPlugin {
    fn version(&self) -> String {
        env!("CARGO_PKG_VERSION").into()
    }

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

struct Motd;

impl SimplePluginCommand for Motd {
    type Plugin = MotdPlugin;

    fn name(&self) -> &str {
        "motd"
    }

    fn description(&self) -> &str {
        "Message of the day"
    }

    fn signature(&self) -> Signature {
        Signature::build(PluginCommand::name(self))
            .input_output_type(Type::Nothing, Type::String)
    }

    fn run(
        &self,
        _plugin: &MotdPlugin,
        engine: &EngineInterface,
        call: &EvaluatedCall,

Title: Implementing PluginCommand and Handling Streams
Summary
This section demonstrates how to implement the `PluginCommand` trait for the `Len` struct to support streaming input, specifically lists. It shows how to modify the `signature()` method to include the `List` type and how to change the input and output types of the `run()` method to `PipelineData`. The section includes error handling for unexpected input types and provides an example of how to process a list stream to calculate the length. It then provides an example of a plugin that transforms a stream, multiplying each integer by two using the `map()` function on the `PipelineData`. Finally, it shows how the configuration can be retrieved with [`EngineInterface::get_plugin_config`](https://docs.rs/nu-plugin/latest/nu_plugin/struct.EngineInterface.html#method.get_plugin_config).