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