nu_command/filters/
zip.rs1use nu_engine::{ClosureEvalOnce, command_prelude::*};
2
3#[derive(Clone)]
4pub struct Zip;
5
6impl Command for Zip {
7 fn name(&self) -> &str {
8 "zip"
9 }
10
11 fn description(&self) -> &str {
12 "Combine a stream with the input."
13 }
14
15 fn signature(&self) -> nu_protocol::Signature {
16 Signature::build("zip")
17 .input_output_types(vec![
18 (
19 Type::List(Box::new(Type::Any)),
20 Type::List(Box::new(Type::List(Box::new(Type::Any)))),
21 ),
22 (
23 Type::Range,
24 Type::List(Box::new(Type::List(Box::new(Type::Any)))),
25 ),
26 ])
27 .required(
28 "other",
29 SyntaxShape::OneOf(vec![SyntaxShape::Any, SyntaxShape::Closure(Some(vec![]))]),
30 "The other input, or closure returning a stream.",
31 )
32 .category(Category::Filters)
33 }
34
35 fn examples(&self) -> Vec<Example> {
36 let test_row_1 = Value::list(
37 vec![Value::test_int(1), Value::test_int(4)],
38 Span::test_data(),
39 );
40
41 let test_row_2 = Value::list(
42 vec![Value::test_int(2), Value::test_int(5)],
43 Span::test_data(),
44 );
45
46 let test_row_3 = Value::list(
47 vec![Value::test_int(3), Value::test_int(6)],
48 Span::test_data(),
49 );
50
51 vec![
52 Example {
53 example: "[1 2] | zip [3 4]",
54 description: "Zip two lists",
55 result: Some(Value::list(
56 vec![
57 Value::list(
58 vec![Value::test_int(1), Value::test_int(3)],
59 Span::test_data(),
60 ),
61 Value::list(
62 vec![Value::test_int(2), Value::test_int(4)],
63 Span::test_data(),
64 ),
65 ],
66 Span::test_data(),
67 )),
68 },
69 Example {
70 example: "1..3 | zip 4..6",
71 description: "Zip two ranges",
72 result: Some(Value::list(
73 vec![test_row_1.clone(), test_row_2.clone(), test_row_3.clone()],
74 Span::test_data(),
75 )),
76 },
77 Example {
78 example: "seq 1 3 | zip { seq 4 600000000 }",
79 description: "Zip two streams",
80 result: Some(Value::list(
81 vec![test_row_1, test_row_2, test_row_3],
82 Span::test_data(),
83 )),
84 },
85 Example {
86 example: "glob *.ogg | zip ['bang.ogg', 'fanfare.ogg', 'laser.ogg'] | each {|| mv $in.0 $in.1 }",
87 description: "Rename .ogg files to match an existing list of filenames",
88 result: None,
89 },
90 ]
91 }
92
93 fn run(
94 &self,
95 engine_state: &EngineState,
96 stack: &mut Stack,
97 call: &Call,
98 input: PipelineData,
99 ) -> Result<PipelineData, ShellError> {
100 let head = call.head;
101 let other = call.req(engine_state, stack, 0)?;
102
103 let metadata = input.metadata();
104 let other = if let Value::Closure { val, .. } = other {
105 ClosureEvalOnce::new(engine_state, stack, *val).run_with_input(PipelineData::empty())?
107 } else {
108 other.into_pipeline_data()
109 };
110
111 Ok(input
112 .into_iter()
113 .zip(other)
114 .map(move |(x, y)| Value::list(vec![x, y], head))
115 .into_pipeline_data_with_metadata(head, engine_state.signals().clone(), metadata))
116 }
117}
118
119#[cfg(test)]
120mod test {
121 use super::*;
122
123 #[test]
124 fn test_examples() {
125 use crate::test_examples;
126
127 test_examples(Zip {})
128 }
129}