Home Explore Blog CI



nushell

7th chunk of `contributor-book/plugins.md`
f30b4e9132f80566ecc7cb8e326534193e2e16076cc83a750000000100001014
            )
            .input_output_type(Type::ListStream, Type::ListStream)
    }

    fn run(
        &self,
        _plugin: &MyEachPlugin,
        engine: &EngineInterface,
        call: &EvaluatedCall,
        input: PipelineData,
    ) -> Result<PipelineData, LabeledError> {
        let engine = engine.clone();
        let closure = call.req(0)?;
        Ok(input.map(move |item| {
            let span = item.span();
            engine.eval_closure(&closure, vec![item.clone()], Some(item))
                .unwrap_or_else(|err| Value::error(err, span))
        }, None)?)
    }
}

fn main() {
    serve_plugin(&MyEachPlugin, MsgPackSerializer)
}
```

`my-each` works just like `each`:

```nu
> [1 2 3] | my-each { |i| $i * 2 }
╭───┬───╮
│ 0 │ 2 │
│ 1 │ 4 │
│ 2 │ 6 │
╰───┴───╯
```

At present, the closures can only refer to values that would be valid to send to the plugin. This means that custom values from other plugins are not allowed. This is likely to be fixed in a future release.

## Custom values

Plugins can create custom values that embed plugin-specific data within the engine. In Rust, this data is automatically serialized using [bincode](https://crates.io/crates/bincode), so all you need to do is implement the [`CustomValue`](https://docs.rs/nu-protocol/latest/nu_protocol/trait.CustomValue.html) trait on a type that has `Serialize` and `Deserialize` implementations compatible with bincode. This includes any attributes that would cause a dependency on field names or field presence, such as `#[serde(skip_serializing_if = "...")]` or `#[serde(untagged)]`. We use the [typetag](https://crates.io/crates/typetag) crate to reconstruct the correct custom value type.

To embed the custom value in a `Value`, use [`Value::custom()`](https://docs.rs/nu-protocol/latest/nu_protocol/enum.Value.html#method.custom_value). A minimal example:

```rust
use nu_protocol::{CustomValue, ShellError, Span, Value, record};
use serde::{Deserialize, Serialize};
use std::any::Any;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Animal {
    Dog {
        name: String,
        woof: bool,
    },
    Cat {
        name: String,
        meow: bool,
    },
}

#[typetag::serde]
impl CustomValue for Animal {
    fn clone_value(&self, span: Span) -> Value {
        Value::custom_value(Box::new(self.clone()), span)
    }

    fn type_name(&self) -> String {
        "Animal".into()
    }

    fn to_base_value(&self, span: Span) -> Result<Value, ShellError> {
        // Construct a simple Nushell value that makes sense here.
        // It must not be a custom value.
        Ok(match self {
            Animal::Dog { name, woof } => Value::record(record! {
                "type" => Value::string("dog", span),
                "name" => Value::string(name, span),
                "woof" => Value::bool(*woof, span),
            }, span),
            Animal::Cat { name, meow } => Value::record(record! {
                "type" => Value::string("cat", span),
                "name" => Value::string(name, span),
                "meow" => Value::bool(*meow, span),
            }, span),
        })
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_mut_any(&mut self) -> &mut dyn Any {
        self
    }
}

// Use the custom value
Value::custom(Box::new(Animal::Dog {
    name: "Rex".into(),
    woof: true,
}), call.head)
```

Any of the methods in the trait can be implemented on plugin custom values, and functionality such as supporting cell paths (e.g. `$my_custom_value.field`), operators (e.g. `++`), and comparisons (e.g. for `sort`) are all supported.

### Drop notification

It is possible to ask Nushell to let you know when all copies of a custom value passed to it have gone out of scope and will no longer be used:

```rust
impl CustomValue for Animal {
    // ...
    fn notify_plugin_on_drop(&self) -> bool {
        true
    }
}
```

The notification is sent to the `Plugin` via [`custom_value_dropped()`](https://docs.rs/nu-plugin/latest/nu_plugin/trait.Plugin.html#method.custom_value_dropped):

Title: Creating and Using Custom Values in Nushell Plugins
Summary
This section explains how plugins can create custom values that embed plugin-specific data within the Nushell engine. It emphasizes the use of the `CustomValue` trait from the `nu-protocol` crate, requiring the implementation of `Serialize` and `Deserialize` traits compatible with bincode. The section provides a detailed example of an `Animal` enum as a custom value, demonstrating how to embed it in a `Value` using `Value::custom_value`. It also touches on the support for cell paths, operators, and comparisons for custom values. Finally, it explains how to receive a drop notification via `custom_value_dropped()` when all copies of a custom value have gone out of scope, by implementing the `notify_plugin_on_drop()` method.