nu_cmd_base/
input_handler.rs

1use nu_protocol::{PipelineData, ShellError, Signals, Span, Value, ast::CellPath};
2use std::sync::Arc;
3
4pub trait CmdArgument {
5    fn take_cell_paths(&mut self) -> Option<Vec<CellPath>>;
6}
7
8/// Arguments with only cell_path.
9///
10/// If commands is going to use `operate` function, and it only required optional cell_paths
11/// Using this to simplify code.
12pub struct CellPathOnlyArgs {
13    cell_paths: Option<Vec<CellPath>>,
14}
15
16impl CmdArgument for CellPathOnlyArgs {
17    fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
18        self.cell_paths.take()
19    }
20}
21
22impl From<Vec<CellPath>> for CellPathOnlyArgs {
23    fn from(cell_paths: Vec<CellPath>) -> Self {
24        Self {
25            cell_paths: (!cell_paths.is_empty()).then_some(cell_paths),
26        }
27    }
28}
29
30/// A simple wrapper for `PipelineData::map` method.
31///
32/// In detail, for each elements, invoking relative `cmd` with `arg`.
33///
34/// If `arg` tell us that its cell path is not None, only map over data under these columns.
35/// Else it will apply each column inside a table.
36///
37/// The validation of input element should be handle by `cmd` itself.
38pub fn operate<C, A>(
39    cmd: C,
40    mut arg: A,
41    input: PipelineData,
42    span: Span,
43    signals: &Signals,
44) -> Result<PipelineData, ShellError>
45where
46    A: CmdArgument + Send + Sync + 'static,
47    C: Fn(&Value, &A, Span) -> Value + Send + Sync + 'static + Clone + Copy,
48{
49    match arg.take_cell_paths() {
50        None => input.map(
51            move |v| {
52                match v {
53                    // Propagate errors inside the input
54                    Value::Error { .. } => v,
55                    _ => cmd(&v, &arg, span),
56                }
57            },
58            signals,
59        ),
60        Some(column_paths) => {
61            let arg = Arc::new(arg);
62            input.map(
63                move |mut v| {
64                    for path in &column_paths {
65                        let opt = arg.clone();
66                        let r = v.update_cell_path(
67                            &path.members,
68                            Box::new(move |old| {
69                                match old {
70                                    // Propagate errors inside the input
71                                    Value::Error { .. } => old.clone(),
72                                    _ => cmd(old, &opt, span),
73                                }
74                            }),
75                        );
76                        if let Err(error) = r {
77                            return Value::error(error, span);
78                        }
79                    }
80                    v
81                },
82                signals,
83            )
84        }
85    }
86}