Skip to main content

nu_command/filters/
peek.rs

1use nu_engine::command_prelude::*;
2use nu_protocol::{PipelineMetadata, casing::WrapCased};
3
4#[derive(Clone)]
5pub struct Peek;
6
7impl Command for Peek {
8    fn name(&self) -> &str {
9        "peek"
10    }
11
12    fn description(&self) -> &str {
13        "Peek the first <n> elements of a stream and store them in the metadata."
14    }
15
16    fn search_terms(&self) -> Vec<&str> {
17        vec!["stream", "inspect"]
18    }
19
20    fn signature(&self) -> Signature {
21        Signature::build("peek")
22            .input_output_types(vec![
23                (
24                    Type::List(Box::new(Type::Any)),
25                    Type::List(Box::new(Type::Any)),
26                ),
27                (Type::table(), Type::table()),
28                (Type::Any, Type::Any),
29            ])
30            .optional(
31                "n",
32                SyntaxShape::Int,
33                "Number of elements to peek, if the input is a stream or list.",
34            )
35    }
36
37    fn run(
38        &self,
39        engine_state: &EngineState,
40        stack: &mut Stack,
41        call: &Call,
42        input: PipelineData,
43    ) -> Result<PipelineData, ShellError> {
44        let n: Option<usize> = call.opt(engine_state, stack, 0)?;
45
46        match input {
47            PipelineData::Empty => {
48                let metadata = add_peek_metadata(None, "empty", false, None, call.head);
49                Ok(Value::nothing(call.head).into_pipeline_data_with_metadata(metadata))
50            }
51            PipelineData::Value(val, metadata) => match &val {
52                Value::List { vals, .. } => {
53                    let peeked = n.map(|n| {
54                        vals.iter()
55                            .take(n)
56                            .cloned()
57                            .collect::<Vec<_>>()
58                            .into_value(call.head)
59                    });
60                    let metadata = add_peek_metadata(metadata, "list", false, peeked, call.head);
61                    Ok(PipelineData::value(val, metadata))
62                }
63                _ => {
64                    let r#type = val.get_type_shallow().get_non_specified_string();
65                    let metadata = add_peek_metadata(metadata, r#type, false, None, call.head);
66                    Ok(PipelineData::value(val, metadata))
67                }
68            },
69            PipelineData::ListStream(stream, metadata) => {
70                let mut elems = None;
71                let stream = match n {
72                    Some(n) => stream.modify(|mut it| {
73                        let collect = it.as_mut().take(n).collect::<Vec<_>>();
74                        elems = Some(collect.clone());
75                        collect.into_iter().chain(it)
76                    }),
77                    None => stream,
78                };
79
80                let metadata = add_peek_metadata(
81                    metadata,
82                    "list",
83                    true,
84                    elems.map(|x| x.into_value(call.head)),
85                    call.head,
86                );
87
88                Ok(PipelineData::list_stream(stream, metadata))
89            }
90            PipelineData::ByteStream(byte_stream, pipeline_metadata) => {
91                let metadata = add_peek_metadata(
92                    pipeline_metadata,
93                    match byte_stream.type_() {
94                        ByteStreamType::Binary => "binary",
95                        ByteStreamType::String => "string",
96                        ByteStreamType::Unknown => "byte stream",
97                    },
98                    true,
99                    None,
100                    call.head,
101                );
102                Ok(PipelineData::byte_stream(byte_stream, metadata))
103            }
104        }
105    }
106
107    fn examples(&self) -> Vec<Example<'_>> {
108        vec![
109            Example {
110                description: "Peek the first 2 elements of a stream.",
111                example: "seq 1 5 | peek 2 | metadata | $in.peek",
112                result: Some(test_record! {
113                    "type" => "list",
114                    "stream" => true,
115                    "value" => [1, 2],
116                }),
117            },
118            Example {
119                description: "Lists can also be peeked.",
120                example: "[1, 2, 3] | peek 1 | metadata | $in.peek",
121                result: Some(test_record! {
122                    "type" => "list",
123                    "stream" => false,
124                    "value" => [1],
125                }),
126            },
127            Example {
128                description: "Peeking non-list values won't return any values.",
129                example: "'hello' | peek 1 | metadata | $in.peek",
130                result: Some(test_record! {
131                    "type" => "string",
132                    "stream" => false,
133                }),
134            },
135            Example {
136                description: "Peeking non-list streams (text streams, binary streams, external byte streams) won't return any values.",
137                example: "[0x[11] 0x[13 15]] | bytes collect | peek | metadata | $in.peek",
138                result: Some(test_record! {
139                    "type" => "binary",
140                    "stream" => true,
141                }),
142            },
143        ]
144    }
145}
146
147fn add_peek_metadata(
148    mut metadata: Option<PipelineMetadata>,
149    r#type: impl Into<String>,
150    stream: bool,
151    value: Option<Value>,
152    span: Span,
153) -> Option<PipelineMetadata> {
154    let mut record = Record::new();
155    let record_handle = record.as_mut().case_sensitive();
156
157    record_handle.insert("type", r#type.into().into_value(span));
158    record_handle.insert("stream", stream.into_value(span));
159    if let Some(value) = value {
160        record_handle.insert("value", value);
161    }
162
163    metadata
164        .get_or_insert_default()
165        .custom
166        .insert("peek", record.into_value(span));
167
168    metadata
169}
170
171#[cfg(test)]
172mod test {
173    use super::*;
174
175    #[test]
176    fn test_examples() -> nu_test_support::Result {
177        nu_test_support::test().examples(Peek)
178    }
179}