nu_command/filters/
items.rs

1use super::utils::chain_error_with_input;
2use nu_engine::{ClosureEval, command_prelude::*};
3use nu_protocol::engine::Closure;
4
5#[derive(Clone)]
6pub struct Items;
7
8impl Command for Items {
9    fn name(&self) -> &str {
10        "items"
11    }
12
13    fn signature(&self) -> Signature {
14        Signature::build(self.name())
15            .input_output_types(vec![(Type::record(), Type::Any)])
16            .required(
17                "closure",
18                SyntaxShape::Closure(Some(vec![SyntaxShape::Any, SyntaxShape::Any])),
19                "The closure to run.",
20            )
21            .allow_variants_without_examples(true)
22            .category(Category::Filters)
23    }
24
25    fn description(&self) -> &str {
26        "Given a record, iterate on each pair of column name and associated value."
27    }
28
29    fn extra_description(&self) -> &str {
30        "This is a the fusion of `columns`, `values` and `each`."
31    }
32
33    fn run(
34        &self,
35        engine_state: &EngineState,
36        stack: &mut Stack,
37        call: &Call,
38        input: PipelineData,
39    ) -> Result<PipelineData, ShellError> {
40        let head = call.head;
41        let closure: Closure = call.req(engine_state, stack, 0)?;
42
43        let metadata = input.metadata();
44        match input {
45            PipelineData::Empty => Ok(PipelineData::empty()),
46            PipelineData::Value(value, ..) => {
47                let span = value.span();
48                match value {
49                    Value::Record { val, .. } => {
50                        let mut closure = ClosureEval::new(engine_state, stack, closure);
51                        Ok(val
52                            .into_owned()
53                            .into_iter()
54                            .map_while(move |(col, val)| {
55                                let result = closure
56                                    .add_arg(Value::string(col, span))
57                                    .add_arg(val)
58                                    .run_with_input(PipelineData::empty())
59                                    .and_then(|data| data.into_value(head));
60
61                                match result {
62                                    Ok(value) => Some(value),
63                                    Err(err) => {
64                                        let err = chain_error_with_input(err, false, span);
65                                        Some(Value::error(err, head))
66                                    }
67                                }
68                            })
69                            .into_pipeline_data(head, engine_state.signals().clone()))
70                    }
71                    Value::Error { error, .. } => Err(*error),
72                    other => Err(ShellError::OnlySupportsThisInputType {
73                        exp_input_type: "record".into(),
74                        wrong_type: other.get_type().to_string(),
75                        dst_span: head,
76                        src_span: other.span(),
77                    }),
78                }
79            }
80            PipelineData::ListStream(stream, ..) => Err(ShellError::OnlySupportsThisInputType {
81                exp_input_type: "record".into(),
82                wrong_type: "stream".into(),
83                dst_span: call.head,
84                src_span: stream.span(),
85            }),
86            PipelineData::ByteStream(stream, ..) => Err(ShellError::OnlySupportsThisInputType {
87                exp_input_type: "record".into(),
88                wrong_type: stream.type_().describe().into(),
89                dst_span: call.head,
90                src_span: stream.span(),
91            }),
92        }
93        .map(|data| data.set_metadata(metadata))
94    }
95
96    fn examples(&self) -> Vec<Example> {
97        vec![Example {
98            example: "{ new: york, san: francisco } | items {|key, value| echo $'($key) ($value)' }",
99            description: "Iterate over each key-value pair of a record",
100            result: Some(Value::list(
101                vec![
102                    Value::test_string("new york"),
103                    Value::test_string("san francisco"),
104                ],
105                Span::test_data(),
106            )),
107        }]
108    }
109}
110
111#[cfg(test)]
112mod test {
113    use super::*;
114
115    #[test]
116    fn test_examples() {
117        use crate::test_examples;
118
119        test_examples(Items {})
120    }
121}