mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 04:05:31 +02:00
Allow plugins to receive configuration from the nushell configuration (#10955)
# Description When nushell calls a plugin it now sends a configuration `Value` from the nushell config under `$env.config.plugins.PLUGIN_SHORT_NAME`. This allows plugin authors to read configuration provided by plugin users. The `PLUGIN_SHORT_NAME` must match the registered filename after `nu_plugin_`. If you register `target/debug/nu_plugin_config` the `PLUGIN_NAME` will be `config` and the nushell config will loook like: $env.config = { # ... plugins: { config: [ some values ] } } Configuration may also use a closure which allows passing values from `$env` to a plugin: $env.config = { # ... plugins: { config: {|| $env.some_value } } } This is a breaking change for the plugin API as the `Plugin::run()` function now accepts a new configuration argument which is an `&Option<Value>`. If no configuration was supplied the value is `None`. Plugins compiled after this change should work with older nushell, and will behave as if the configuration was not set. Initially discussed in #10867 # User-Facing Changes * Plugins can read configuration data stored in `$env.config.plugins` * The plugin `CallInfo` now includes a `config` entry, existing plugins will require updates # Tests + Formatting - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib` # After Submitting - [ ] Update [Creating a plugin (in Rust)](https://www.nushell.sh/contributor-book/plugins.html#creating-a-plugin-in-rust) [source](https://github.com/nushell/nushell.github.io/blob/main/contributor-book/plugins.md) - [ ] Add "Configuration" section to [Plugins documentation](https://www.nushell.sh/contributor-book/plugins.html)
This commit is contained in:
@ -28,6 +28,7 @@
|
||||
//! fn run(
|
||||
//! &mut self,
|
||||
//! name: &str,
|
||||
//! config: &Option<Value>,
|
||||
//! call: &EvaluatedCall,
|
||||
//! input: &Value
|
||||
//! ) -> Result<Value, LabeledError> {
|
||||
|
@ -6,6 +6,7 @@ use crate::protocol::{
|
||||
};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use nu_engine::eval_block;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{ast::Call, PluginSignature, Signature};
|
||||
use nu_protocol::{Example, PipelineData, ShellError, Value};
|
||||
@ -126,10 +127,48 @@ impl Command for PluginDeclaration {
|
||||
value => CallInput::Value(value),
|
||||
};
|
||||
|
||||
// Fetch the configuration for a plugin
|
||||
//
|
||||
// The `plugin` must match the registered name of a plugin. For
|
||||
// `register nu_plugin_example` the plugin config lookup uses `"example"`
|
||||
let config = self
|
||||
.filename
|
||||
.file_stem()
|
||||
.and_then(|file| {
|
||||
file.to_string_lossy()
|
||||
.clone()
|
||||
.strip_prefix("nu_plugin_")
|
||||
.map(|name| {
|
||||
nu_engine::get_config(engine_state, stack)
|
||||
.plugins
|
||||
.get(name)
|
||||
.cloned()
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
.map(|value| {
|
||||
let span = value.span();
|
||||
match value {
|
||||
Value::Closure { val, .. } => {
|
||||
let input = PipelineData::Empty;
|
||||
|
||||
let block = engine_state.get_block(val.block_id).clone();
|
||||
let mut stack = stack.captures_to_stack(val.captures);
|
||||
|
||||
match eval_block(engine_state, &mut stack, &block, input, false, false) {
|
||||
Ok(v) => v.into_value(span),
|
||||
Err(e) => Value::error(e, call.head),
|
||||
}
|
||||
}
|
||||
_ => value.clone(),
|
||||
}
|
||||
});
|
||||
|
||||
let plugin_call = PluginCall::CallInfo(CallInfo {
|
||||
name: self.name.clone(),
|
||||
call: EvaluatedCall::try_from_call(call, engine_state, stack)?,
|
||||
input,
|
||||
config,
|
||||
});
|
||||
|
||||
let encoding = {
|
||||
|
@ -217,6 +217,7 @@ pub fn get_signature(
|
||||
/// fn run(
|
||||
/// &mut self,
|
||||
/// name: &str,
|
||||
/// config: &Option<Value>,
|
||||
/// call: &EvaluatedCall,
|
||||
/// input: &Value,
|
||||
/// ) -> Result<Value, LabeledError> {
|
||||
@ -246,6 +247,7 @@ pub trait Plugin {
|
||||
fn run(
|
||||
&mut self,
|
||||
name: &str,
|
||||
config: &Option<Value>,
|
||||
call: &EvaluatedCall,
|
||||
input: &Value,
|
||||
) -> Result<Value, LabeledError>;
|
||||
@ -264,7 +266,7 @@ pub trait Plugin {
|
||||
/// # impl MyPlugin { fn new() -> Self { Self }}
|
||||
/// # impl Plugin for MyPlugin {
|
||||
/// # fn signature(&self) -> Vec<PluginSignature> {todo!();}
|
||||
/// # fn run(&mut self, name: &str, call: &EvaluatedCall, input: &Value)
|
||||
/// # fn run(&mut self, name: &str, config: &Option<Value>, call: &EvaluatedCall, input: &Value)
|
||||
/// # -> Result<Value, LabeledError> {todo!();}
|
||||
/// # }
|
||||
/// fn main() {
|
||||
@ -333,7 +335,9 @@ pub fn serve_plugin(plugin: &mut impl Plugin, encoder: impl PluginEncoder) {
|
||||
};
|
||||
|
||||
let value = match input {
|
||||
Ok(input) => plugin.run(&call_info.name, &call_info.call, &input),
|
||||
Ok(input) => {
|
||||
plugin.run(&call_info.name, &call_info.config, &call_info.call, &input)
|
||||
}
|
||||
Err(err) => Err(err.into()),
|
||||
};
|
||||
|
||||
|
@ -13,6 +13,7 @@ pub struct CallInfo {
|
||||
pub name: String,
|
||||
pub call: EvaluatedCall,
|
||||
pub input: CallInput,
|
||||
pub config: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
|
@ -106,6 +106,7 @@ mod tests {
|
||||
name: name.clone(),
|
||||
call: call.clone(),
|
||||
input: CallInput::Value(input.clone()),
|
||||
config: None,
|
||||
});
|
||||
|
||||
let encoder = JsonSerializer {};
|
||||
|
@ -107,6 +107,7 @@ mod tests {
|
||||
name: name.clone(),
|
||||
call: call.clone(),
|
||||
input: CallInput::Value(input.clone()),
|
||||
config: None,
|
||||
});
|
||||
|
||||
let encoder = MsgPackSerializer {};
|
||||
|
Reference in New Issue
Block a user