nu_command/filters/
slice.rs

1use nu_engine::command_prelude::*;
2use nu_protocol::IntRange;
3use std::ops::Bound;
4
5#[derive(Clone)]
6pub struct Slice;
7
8impl Command for Slice {
9    fn name(&self) -> &str {
10        "slice"
11    }
12
13    fn signature(&self) -> Signature {
14        Signature::build("slice")
15            .input_output_types(vec![(
16                Type::List(Box::new(Type::Any)),
17                Type::List(Box::new(Type::Any)),
18            )])
19            .required("rows", SyntaxShape::Range, "Range of rows to return.")
20            .category(Category::Filters)
21    }
22
23    fn description(&self) -> &str {
24        "Return only the selected rows."
25    }
26
27    fn search_terms(&self) -> Vec<&str> {
28        vec!["filter", "head", "tail", "range"]
29    }
30
31    fn examples(&self) -> Vec<Example> {
32        vec![
33            Example {
34                example: "[0,1,2,3,4,5] | slice 4..5",
35                description: "Get the last 2 items",
36                result: Some(Value::list(
37                    vec![Value::test_int(4), Value::test_int(5)],
38                    Span::test_data(),
39                )),
40            },
41            Example {
42                example: "[0,1,2,3,4,5] | slice (-2)..",
43                description: "Get the last 2 items",
44                result: Some(Value::list(
45                    vec![Value::test_int(4), Value::test_int(5)],
46                    Span::test_data(),
47                )),
48            },
49            Example {
50                example: "[0,1,2,3,4,5] | slice (-3)..-2",
51                description: "Get the next to last 2 items",
52                result: Some(Value::list(
53                    vec![Value::test_int(3), Value::test_int(4)],
54                    Span::test_data(),
55                )),
56            },
57        ]
58    }
59
60    fn run(
61        &self,
62        engine_state: &EngineState,
63        stack: &mut Stack,
64        call: &Call,
65        input: PipelineData,
66    ) -> Result<PipelineData, ShellError> {
67        let head = call.head;
68        let metadata = input.metadata();
69        let range: IntRange = call.req(engine_state, stack, 0)?;
70
71        // only collect the input if we have any negative indices
72        if range.is_relative() {
73            let v: Vec<_> = input.into_iter().collect();
74
75            let (from, to) = range.absolute_bounds(v.len());
76
77            let count = match to {
78                Bound::Excluded(to) => to.saturating_sub(from),
79                Bound::Included(to) => to.saturating_sub(from) + 1,
80                Bound::Unbounded => usize::MAX,
81            };
82
83            if count == 0 {
84                Ok(PipelineData::value(Value::list(vec![], head), None))
85            } else {
86                let iter = v.into_iter().skip(from).take(count);
87                Ok(iter.into_pipeline_data(head, engine_state.signals().clone()))
88            }
89        } else {
90            let from = range.start() as usize;
91            let count = match range.end() {
92                Bound::Excluded(to) | Bound::Included(to) if range.start() > to => 0,
93                Bound::Excluded(to) => (to as usize).saturating_sub(from),
94                Bound::Included(to) => (to as usize).saturating_sub(from) + 1,
95                Bound::Unbounded => {
96                    if range.step() < 0 {
97                        0
98                    } else {
99                        usize::MAX
100                    }
101                }
102            };
103
104            if count == 0 {
105                Ok(PipelineData::value(Value::list(vec![], head), None))
106            } else {
107                let iter = input.into_iter().skip(from).take(count);
108                Ok(iter.into_pipeline_data(head, engine_state.signals().clone()))
109            }
110        }
111        .map(|x| x.set_metadata(metadata))
112    }
113}
114
115#[cfg(test)]
116mod test {
117    use super::*;
118
119    #[test]
120    fn test_examples() {
121        use crate::test_examples;
122
123        test_examples(Slice {})
124    }
125}