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