nu_command/filters/
last.rs1use nu_engine::command_prelude::*;
2use nu_protocol::shell_error::io::IoError;
3use std::{collections::VecDeque, io::Read};
4
5#[derive(Clone)]
6pub struct Last;
7
8impl Command for Last {
9 fn name(&self) -> &str {
10 "last"
11 }
12
13 fn signature(&self) -> Signature {
14 Signature::build("last")
15 .input_output_types(vec![
16 (
17 Type::List(Box::new(Type::Any)),
20 Type::Any,
21 ),
22 (Type::Binary, Type::Binary),
23 (Type::Range, Type::Any),
24 ])
25 .optional(
26 "rows",
27 SyntaxShape::Int,
28 "Starting from the back, the number of rows to return.",
29 )
30 .switch("strict", "Throw an error if input is empty", Some('s'))
31 .category(Category::Filters)
32 }
33
34 fn description(&self) -> &str {
35 "Return only the last several rows of the input. Counterpart of `first`. Opposite of `drop`."
36 }
37
38 fn examples(&self) -> Vec<Example<'_>> {
39 vec![
40 Example {
41 example: "[1,2,3] | last 2",
42 description: "Return the last 2 items of a list/table",
43 result: Some(Value::list(
44 vec![Value::test_int(2), Value::test_int(3)],
45 Span::test_data(),
46 )),
47 },
48 Example {
49 example: "[1,2,3] | last",
50 description: "Return the last item of a list/table",
51 result: Some(Value::test_int(3)),
52 },
53 Example {
54 example: "0x[01 23 45] | last 2",
55 description: "Return the last 2 bytes of a binary value",
56 result: Some(Value::binary(vec![0x23, 0x45], Span::test_data())),
57 },
58 Example {
59 example: "1..3 | last",
60 description: "Return the last item of a range",
61 result: Some(Value::test_int(3)),
62 },
63 ]
64 }
65
66 fn run(
67 &self,
68 engine_state: &EngineState,
69 stack: &mut Stack,
70 call: &Call,
71 input: PipelineData,
72 ) -> Result<PipelineData, ShellError> {
73 let head = call.head;
74 let rows: Option<Spanned<i64>> = call.opt(engine_state, stack, 0)?;
75 let strict_mode = call.has_flag(engine_state, stack, "strict")?;
76
77 let return_single_element = rows.is_none();
80 let rows = if let Some(rows) = rows {
81 if rows.item < 0 {
82 return Err(ShellError::NeedsPositiveValue { span: rows.span });
83 } else {
84 rows.item as usize
85 }
86 } else {
87 1
88 };
89
90 let metadata = input.metadata();
91
92 if rows == 0 {
94 return Ok(Value::list(Vec::new(), head).into_pipeline_data_with_metadata(metadata));
95 }
96
97 match input {
98 PipelineData::ListStream(_, _) | PipelineData::Value(Value::Range { .. }, _) => {
99 let iterator = input.into_iter_strict(head)?;
100
101 let mut buf = VecDeque::new();
103
104 for row in iterator {
105 engine_state.signals().check(&head)?;
106 if buf.len() == rows {
107 buf.pop_front();
108 }
109 buf.push_back(row);
110 }
111
112 if return_single_element {
113 if let Some(last) = buf.pop_back() {
114 Ok(last.into_pipeline_data())
115 } else if strict_mode {
116 Err(ShellError::AccessEmptyContent { span: head })
117 } else {
118 Ok(Value::nothing(head).into_pipeline_data_with_metadata(metadata))
121 }
122 } else {
123 Ok(Value::list(buf.into(), head).into_pipeline_data_with_metadata(metadata))
124 }
125 }
126 PipelineData::Value(val, _) => {
127 let span = val.span();
128 match val {
129 Value::List { mut vals, .. } => {
130 if return_single_element {
131 if let Some(v) = vals.pop() {
132 Ok(v.into_pipeline_data())
133 } else if strict_mode {
134 Err(ShellError::AccessEmptyContent { span: head })
135 } else {
136 Ok(Value::nothing(head).into_pipeline_data_with_metadata(metadata))
139 }
140 } else {
141 let i = vals.len().saturating_sub(rows);
142 vals.drain(..i);
143 Ok(Value::list(vals, span).into_pipeline_data_with_metadata(metadata))
144 }
145 }
146 Value::Binary { mut val, .. } => {
147 if return_single_element {
148 if let Some(val) = val.pop() {
149 Ok(Value::int(val.into(), span).into_pipeline_data())
150 } else if strict_mode {
151 Err(ShellError::AccessEmptyContent { span: head })
152 } else {
153 Ok(Value::nothing(head).into_pipeline_data_with_metadata(metadata))
156 }
157 } else {
158 let i = val.len().saturating_sub(rows);
159 val.drain(..i);
160 Ok(Value::binary(val, span).into_pipeline_data())
161 }
162 }
163 Value::Error { error, .. } => Err(*error),
165 other => Err(ShellError::OnlySupportsThisInputType {
166 exp_input_type: "list, binary or range".into(),
167 wrong_type: other.get_type().to_string(),
168 dst_span: head,
169 src_span: other.span(),
170 }),
171 }
172 }
173 PipelineData::ByteStream(stream, ..) => {
174 if stream.type_().is_binary_coercible() {
175 let span = stream.span();
176 if let Some(mut reader) = stream.reader() {
177 const TAKE: u64 = 8192;
180 let mut buf = VecDeque::with_capacity(rows + TAKE as usize);
181 loop {
182 let taken = std::io::copy(&mut (&mut reader).take(TAKE), &mut buf)
183 .map_err(|err| IoError::new(err, span, None))?;
184 if buf.len() > rows {
185 buf.drain(..(buf.len() - rows));
186 }
187 if taken < TAKE {
188 if return_single_element {
190 if !buf.is_empty() {
191 return Ok(
192 Value::int(buf[0] as i64, head).into_pipeline_data()
193 );
194 } else if strict_mode {
195 return Err(ShellError::AccessEmptyContent { span: head });
196 } else {
197 return Ok(Value::nothing(head)
200 .into_pipeline_data_with_metadata(metadata));
201 }
202 } else {
203 return Ok(Value::binary(buf, head).into_pipeline_data());
204 }
205 }
206 }
207 } else {
208 Ok(PipelineData::empty())
209 }
210 } else {
211 Err(ShellError::OnlySupportsThisInputType {
212 exp_input_type: "list, binary or range".into(),
213 wrong_type: stream.type_().describe().into(),
214 dst_span: head,
215 src_span: stream.span(),
216 })
217 }
218 }
219 PipelineData::Empty => Err(ShellError::OnlySupportsThisInputType {
220 exp_input_type: "list, binary or range".into(),
221 wrong_type: "null".into(),
222 dst_span: call.head,
223 src_span: call.head,
224 }),
225 }
226 }
227}
228
229#[cfg(test)]
230mod test {
231 use super::*;
232
233 #[test]
234 fn test_examples() {
235 use crate::test_examples;
236
237 test_examples(Last {})
238 }
239}