Skip to main content

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 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        mut 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.take_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                                    .and_then(|closure| closure.add_arg(val))
58                                    .and_then(|closure| {
59                                        closure.run_with_input(PipelineData::empty())
60                                    })
61                                    .and_then(|data| data.into_value(head));
62
63                                match result {
64                                    Ok(value) => Some(value),
65                                    Err(err) => {
66                                        let err = chain_error_with_input(err, false, span);
67                                        Some(Value::error(err, head))
68                                    }
69                                }
70                            })
71                            .into_pipeline_data(head, engine_state.signals().clone()))
72                    }
73                    Value::Error { error, .. } => Err(*error),
74                    other => Err(ShellError::OnlySupportsThisInputType {
75                        exp_input_type: "record".into(),
76                        wrong_type: other.get_type().to_string(),
77                        dst_span: head,
78                        src_span: other.span(),
79                    }),
80                }
81            }
82            PipelineData::ListStream(stream, ..) => Err(ShellError::OnlySupportsThisInputType {
83                exp_input_type: "record".into(),
84                wrong_type: "stream".into(),
85                dst_span: call.head,
86                src_span: stream.span(),
87            }),
88            PipelineData::ByteStream(stream, ..) => Err(ShellError::OnlySupportsThisInputType {
89                exp_input_type: "record".into(),
90                wrong_type: stream.type_().describe().into(),
91                dst_span: call.head,
92                src_span: stream.span(),
93            }),
94        }
95        .map(|data| data.set_metadata(metadata))
96    }
97
98    fn examples(&self) -> Vec<Example<'_>> {
99        vec![Example {
100            example: "{ new: york, san: francisco } | items {|key, value| echo $'($key) ($value)' }",
101            description: "Iterate over each key-value pair of a record",
102            result: Some(Value::list(
103                vec![
104                    Value::test_string("new york"),
105                    Value::test_string("san francisco"),
106                ],
107                Span::test_data(),
108            )),
109        }]
110    }
111}
112
113#[cfg(test)]
114mod test {
115    use super::*;
116
117    #[test]
118    fn test_examples() -> nu_test_support::Result {
119        nu_test_support::test().examples(Items)
120    }
121}