nu_command/filters/
items.rs1use 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}