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}