Skip to main content

nu_protocol/engine/
command.rs

1use serde::{Deserialize, Serialize};
2
3use super::{EngineState, Stack, StateWorkingSet};
4use crate::{
5    Alias, BlockId, DeprecationEntry, DynamicCompletionCallRef, DynamicSuggestion, Example,
6    OutDest, PipelineData, ShellError, Signature, Span, Value, engine::Call,
7};
8use std::{
9    any::Any,
10    borrow::Cow,
11    fmt::{Debug, Display},
12};
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum ArgType<'a> {
16    Flag(Cow<'a, str>),
17    Positional(usize),
18}
19
20impl<'a> Display for ArgType<'a> {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        match self {
23            ArgType::Flag(flag_name) => match flag_name {
24                Cow::Borrowed(v) => write!(f, "{v}"),
25                Cow::Owned(v) => write!(f, "{v}"),
26            },
27            ArgType::Positional(idx) => write!(f, "{idx}"),
28        }
29    }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
33pub enum CommandType {
34    Builtin,
35    Custom,
36    Keyword,
37    External,
38    Alias,
39    Plugin,
40}
41
42impl Display for CommandType {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        let str = match self {
45            CommandType::Builtin => "built-in",
46            CommandType::Custom => "custom",
47            CommandType::Keyword => "keyword",
48            CommandType::External => "external",
49            CommandType::Alias => "alias",
50            CommandType::Plugin => "plugin",
51        };
52        write!(f, "{str}")
53    }
54}
55
56pub trait Command: Send + Sync + CommandClone + Any {
57    fn name(&self) -> &str;
58
59    fn signature(&self) -> Signature;
60
61    /// Short preferably single sentence description for the command.
62    ///
63    /// Will be shown with the completions etc.
64    fn description(&self) -> &str;
65
66    /// Longer documentation description, if necessary.
67    ///
68    /// Will be shown below `description`
69    fn extra_description(&self) -> &str {
70        ""
71    }
72
73    fn run(
74        &self,
75        engine_state: &EngineState,
76        stack: &mut Stack,
77        call: &Call,
78        input: PipelineData,
79    ) -> Result<PipelineData, ShellError>;
80
81    /// Used by the parser to run command at parse time
82    ///
83    /// If a command has `is_const()` set to true, it must also implement this method.
84    #[allow(unused_variables)]
85    fn run_const(
86        &self,
87        working_set: &StateWorkingSet,
88        call: &Call,
89        input: PipelineData,
90    ) -> Result<PipelineData, ShellError> {
91        Err(ShellError::MissingConstEvalImpl { span: call.head })
92    }
93
94    fn examples(&self) -> Vec<Example<'_>> {
95        Vec::new()
96    }
97
98    // Related terms to help with command search
99    fn search_terms(&self) -> Vec<&str> {
100        vec![]
101    }
102
103    fn attributes(&self) -> Vec<(String, Value)> {
104        vec![]
105    }
106
107    // Whether can run in const evaluation in the parser
108    fn is_const(&self) -> bool {
109        false
110    }
111
112    // Is a sub command
113    fn is_sub(&self) -> bool {
114        self.name().contains(' ')
115    }
116
117    // If command is a block i.e. def blah [] { }, get the block id
118    fn block_id(&self) -> Option<BlockId> {
119        None
120    }
121
122    // Return reference to the command as Alias
123    fn as_alias(&self) -> Option<&Alias> {
124        None
125    }
126
127    /// The identity of the plugin, if this is a plugin command
128    #[cfg(feature = "plugin")]
129    fn plugin_identity(&self) -> Option<&crate::PluginIdentity> {
130        None
131    }
132
133    fn command_type(&self) -> CommandType {
134        CommandType::Builtin
135    }
136
137    fn is_builtin(&self) -> bool {
138        self.command_type() == CommandType::Builtin
139    }
140
141    fn is_custom(&self) -> bool {
142        self.command_type() == CommandType::Custom
143    }
144
145    fn is_keyword(&self) -> bool {
146        self.command_type() == CommandType::Keyword
147    }
148
149    fn is_known_external(&self) -> bool {
150        self.command_type() == CommandType::External
151    }
152
153    /// The span of this command's declaration, if available.
154    /// Used to look up the source file where the command was declared.
155    /// Applicable to any command type that knows its declaration site.
156    fn decl_span(&self) -> Option<Span> {
157        None
158    }
159
160    fn is_alias(&self) -> bool {
161        self.command_type() == CommandType::Alias
162    }
163
164    fn is_plugin(&self) -> bool {
165        self.command_type() == CommandType::Plugin
166    }
167
168    fn deprecation_info(&self) -> Vec<DeprecationEntry> {
169        vec![]
170    }
171
172    fn pipe_redirection(&self) -> (Option<OutDest>, Option<OutDest>) {
173        (None, None)
174    }
175
176    // engine_state and stack are required to get completion from plugin.
177    /// Get completion items for `arg_type`.
178    ///
179    /// It's useful when you want to get auto completion items of a flag or positional argument
180    /// dynamically.
181    ///
182    /// The implementation can returns 3 types of return values:
183    /// - None: I couldn't find any suggestions, please fall back to default completions
184    /// - Some(vec![]): there are no suggestions
185    /// - Some(vec![item1, item2]): item1 and item2 are available
186    #[allow(unused_variables)]
187    #[expect(deprecated)]
188    fn get_dynamic_completion(
189        &self,
190        engine_state: &EngineState,
191        stack: &mut Stack,
192        call: DynamicCompletionCallRef,
193        arg_type: &ArgType,
194        _experimental: ExperimentalMarker,
195    ) -> Result<Option<Vec<DynamicSuggestion>>, ShellError> {
196        Ok(None)
197    }
198
199    /// Return true if the AST nodes for the arguments are required for IR evaluation. This is
200    /// currently inefficient so is not generally done.
201    fn requires_ast_for_arguments(&self) -> bool {
202        false
203    }
204}
205
206pub trait CommandClone {
207    fn clone_box(&self) -> Box<dyn Command>;
208}
209
210impl<T> CommandClone for T
211where
212    T: 'static + Command + Clone,
213{
214    fn clone_box(&self) -> Box<dyn Command> {
215        Box::new(self.clone())
216    }
217}
218
219impl Clone for Box<dyn Command> {
220    fn clone(&self) -> Box<dyn Command> {
221        self.clone_box()
222    }
223}
224
225/// Marker type for tagging [`Command`] methods as experimental.
226///
227/// Add this marker as a parameter to a method to make implementors see a deprecation warning when
228/// they implement it.
229#[derive(Debug, Clone, Copy, PartialEq, Eq)]
230#[deprecated(note = "this method is very experimental, likely to change")]
231pub struct ExperimentalMarker;