"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):
```rust
impl Plugin for AnimalPlugin {
// ...
fn custom_value_dropped(
&self,
engine: &EngineInterface,
custom_value: Box<dyn CustomValue>,
) {
if let Some(animal) = custom_value.as_any().downcast_ref::<Animal>() {
eprintln!("Animal dropped: {:?}", animal);
}
}
}
```
Every custom value sent from the plugin to the engine counts as a new unique value for the purpose of drop checking. If you accept a custom value as an argument and then return it after, you will likely receive two drop notifications, even though the value data is identical. This has implications for trying to use custom values to reference count handles.
For a full example, see [`DropCheck`](https://github.com/nushell/nushell/blob/main/crates/nu_plugin_custom_values/src/drop_check.rs) in the `nu_plugin_custom_values` plugin.
## Manipulating the environment
Environment variables can be get or set through the [`EngineInterface`](https://docs.rs/nu-plugin/latest/nu_plugin/struct.EngineInterface.html). For example:
```rust
// Get the PATH environment variable
let paths: Value = engine.get_env_var("PATH")?;
// Get all environment variables
let envs: HashMap<String, Value> = engine.get_env_vars()?;
// Set an environment variable
engine.add_env_var("FOO", Value::string("bar", call.head))?;
```
Environment variables set during a plugin call are available in the caller's scope after the plugin call returns, and are also visible to other engine calls (such as closure evaluations) during the plugin call. Setting an environment variable after the plugin call has returned a response - for example while a stream is being produced as the result of a plugin call - has no impact on the environment of the caller's scope.
### Current directory
As noted earlier in the [Launch environment](#launch-environment) section, plugins are always started in the directory of their executable. This is intentionally done to try to ensure the current directory of the shell context is handled correctly. For plugins that work with filesystem paths, relative paths should always be joined against the path returned by [`EngineInterface::get_current_dir()`](https://docs.rs/nu-plugin/latest/nu_plugin/struct.EngineInterface.html#method.get_current_dir):
```rust
use std::path::Path;
use nu_protocol::Spanned;
let relative_path: Spanned<String> = call.req(0)?;
let absolute_path = Path::new(&engine.get_current_dir()?).join(&provided_path.item);
// For example:
if absolute_path.exists() {
return Err(
LabeledError::new(format!("{} does not exist", absolute_path.display()))
.with_label("file not found", relative_path.span)
);
}
```
Note that it is not safe (at least in Rust) to change the plugin's process working directory (e.g. with `std::env::set_current_dir()`) to the current directory from the call context, as multiple threads could be processing calls in different working directories simultaneously.