use nu_engine::{command_prelude::*, get_eval_expression};
use nu_plugin_protocol::{
CallInfo, DynamicCompletionCall, EvaluatedCall, GetCompletionArgType, GetCompletionInfo,
};
use nu_protocol::engine::ArgType;
use nu_protocol::shell_error::generic::GenericError;
use nu_protocol::{DynamicCompletionCallRef, DynamicSuggestion};
use nu_protocol::{PluginIdentity, PluginSignature, engine::CommandType};
use std::sync::Arc;
use crate::{GetPlugin, PluginExecutionCommandContext, PluginSource};
#[derive(Clone)]
pub struct PluginDeclaration {
name: String,
signature: PluginSignature,
source: PluginSource,
}
impl PluginDeclaration {
pub fn new(plugin: Arc<dyn GetPlugin>, signature: PluginSignature) -> Self {
Self {
name: signature.sig.name.clone(),
signature,
source: PluginSource::new(plugin),
}
}
}
impl Command for PluginDeclaration {
fn name(&self) -> &str {
&self.name
}
fn signature(&self) -> Signature {
self.signature.sig.clone()
}
fn description(&self) -> &str {
self.signature.sig.description.as_str()
}
fn extra_description(&self) -> &str {
self.signature.sig.extra_description.as_str()
}
fn search_terms(&self) -> Vec<&str> {
self.signature
.sig
.search_terms
.iter()
.map(|term| term.as_str())
.collect()
}
fn examples(&self) -> Vec<Example<'_>> {
let mut res = vec![];
for e in self.signature.examples.iter() {
res.push(Example {
example: &e.example,
description: &e.description,
result: e.result.clone(),
})
}
res
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let eval_expression = get_eval_expression(engine_state);
let evaluated_call =
EvaluatedCall::try_from_call(call, engine_state, stack, eval_expression)?;
let engine_config = stack.get_config(engine_state);
let plugin = self
.source
.persistent(None)
.and_then(|p| {
p.set_gc_config(engine_config.plugin_gc.get(p.identity().name()));
p.get_plugin(Some((engine_state, stack)))
})
.map_err(|err| {
let decl = engine_state.get_decl(call.decl_id);
ShellError::Generic(GenericError::new(
format!("Unable to spawn plugin for `{}`", decl.name()),
err.to_string(),
call.head,
))
})?;
let mut context = PluginExecutionCommandContext::new(
self.source.identity.clone(),
engine_state,
stack,
call,
);
plugin.run(
CallInfo {
name: self.name.clone(),
call: evaluated_call,
input,
},
&mut context,
)
}
fn command_type(&self) -> CommandType {
CommandType::Plugin
}
fn plugin_identity(&self) -> Option<&PluginIdentity> {
Some(&self.source.identity)
}
#[expect(deprecated, reason = "internal usage")]
fn get_dynamic_completion(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: DynamicCompletionCallRef,
arg_type: &ArgType,
_experimental: nu_protocol::engine::ExperimentalMarker,
) -> Result<Option<Vec<DynamicSuggestion>>, ShellError> {
let engine_config = stack.get_config(engine_state);
let plugin = self
.source
.persistent(None)
.and_then(|p| {
p.set_gc_config(engine_config.plugin_gc.get(p.identity().name()));
p.get_plugin(Some((engine_state, stack)))
})
.map_err(|err| {
ShellError::Generic(GenericError::new_internal(
"failed to get custom completion",
err.to_string(),
))
})?;
let arg_info = match arg_type {
ArgType::Flag(flag_name) => GetCompletionArgType::Flag(flag_name.to_string()),
ArgType::Positional(index) => GetCompletionArgType::Positional(*index),
};
plugin.get_dynamic_completion(GetCompletionInfo {
name: self.name.clone(),
arg_type: arg_info,
call: DynamicCompletionCall {
call: call.call.clone(),
strip: call.strip,
pos: call.pos,
},
})
}
}