nu_cmd_base/
wrap_call.rs

1use nu_engine::CallExt;
2use nu_protocol::{
3    DeclId, FromValue, ShellError, Span,
4    engine::{Call, EngineState, Stack, StateWorkingSet},
5};
6
7/// A helper utility to aid in implementing commands which have the same behavior for `run` and `run_const`.
8///
9/// Only supports functions in [`Call`] and [`CallExt`] which have a `const` suffix.
10///
11/// To use, the actual command logic should be moved to a function. Then, `eval` and `eval_const` can be implemented like this:
12/// ```rust
13/// # use nu_engine::command_prelude::*;
14/// # use nu_cmd_base::WrapCall;
15/// # fn do_command_logic(call: WrapCall) -> Result<PipelineData, ShellError> { Ok(PipelineData::empty()) }
16///
17/// # struct Command {}
18/// # impl Command {
19/// fn run(&self, engine_state: &EngineState, stack: &mut Stack, call: &Call) -> Result<PipelineData, ShellError> {
20///     let call = WrapCall::Eval(engine_state, stack, call);
21///     do_command_logic(call)
22/// }
23///
24/// fn run_const(&self, working_set: &StateWorkingSet, call: &Call) -> Result<PipelineData, ShellError> {
25///     let call = WrapCall::ConstEval(working_set, call);
26///     do_command_logic(call)
27/// }
28/// # }
29/// ```
30///
31/// Then, the typical [`Call`] and [`CallExt`] operations can be called using destructuring:
32///
33/// ```rust
34/// # use nu_engine::command_prelude::*;
35/// # use nu_cmd_base::WrapCall;
36/// # let call = WrapCall::Eval(&EngineState::new(), &mut Stack::new(), &Call::new(Span::unknown()));
37/// # fn do_command_logic(call: WrapCall) -> Result<(), ShellError> {
38/// let (call, required): (_, String) = call.req(0)?;
39/// let (call, flag): (_, Option<i64>) = call.get_flag("number")?;
40/// # Ok(())
41/// # }
42/// ```
43///
44/// A new `WrapCall` instance has to be returned after each function to ensure
45/// that there is only ever one copy of mutable [`Stack`] reference.
46pub enum WrapCall<'a> {
47    Eval(&'a EngineState, &'a mut Stack, &'a Call<'a>),
48    ConstEval(&'a StateWorkingSet<'a>, &'a Call<'a>),
49}
50
51/// Macro to choose between the non-const and const versions of each [`Call`]/[`CallExt`] function
52macro_rules! proxy {
53    ($self:ident , $eval:ident , $const:ident , $( $args:expr ),*) => {
54        match $self {
55            WrapCall::Eval(engine_state, stack, call) => {
56                Call::$eval(call, engine_state, stack, $( $args ),*)
57                .map(|val| (WrapCall::Eval(engine_state, stack, call), val))
58            },
59            WrapCall::ConstEval(working_set, call) => {
60                Call::$const(call, working_set, $( $args ),*)
61                .map(|val| (WrapCall::ConstEval(working_set, call), val))
62            },
63        }
64    };
65}
66
67impl WrapCall<'_> {
68    pub fn head(&self) -> Span {
69        match self {
70            WrapCall::Eval(_, _, call) => call.head,
71            WrapCall::ConstEval(_, call) => call.head,
72        }
73    }
74
75    pub fn decl_id(&self) -> DeclId {
76        match self {
77            WrapCall::Eval(_, _, call) => call.decl_id,
78            WrapCall::ConstEval(_, call) => call.decl_id,
79        }
80    }
81
82    pub fn has_flag<T: FromValue>(self, flag_name: &str) -> Result<(Self, bool), ShellError> {
83        proxy!(self, has_flag, has_flag_const, flag_name)
84    }
85
86    pub fn get_flag<T: FromValue>(self, name: &str) -> Result<(Self, Option<T>), ShellError> {
87        proxy!(self, get_flag, get_flag_const, name)
88    }
89
90    pub fn req<T: FromValue>(self, pos: usize) -> Result<(Self, T), ShellError> {
91        proxy!(self, req, req_const, pos)
92    }
93
94    pub fn rest<T: FromValue>(self, pos: usize) -> Result<(Self, Vec<T>), ShellError> {
95        proxy!(self, rest, rest_const, pos)
96    }
97
98    pub fn opt<T: FromValue>(self, pos: usize) -> Result<(Self, Option<T>), ShellError> {
99        proxy!(self, opt, opt_const, pos)
100    }
101}