1#[cfg(feature = "sqlite")]
2use crate::database::QueryPlan;
3use nu_engine::command_prelude::*;
4use nu_protocol::shell_error::io::IoError;
5use std::{collections::VecDeque, io::Read};
6
7#[derive(Clone)]
8pub struct Last;
9
10impl Command for Last {
11 fn name(&self) -> &str {
12 "last"
13 }
14
15 fn signature(&self) -> Signature {
16 Signature::build("last")
17 .input_output_types(vec![
18 (
19 Type::List(Box::new(Type::Any)),
22 Type::Any,
23 ),
24 (Type::Binary, Type::Binary),
25 (Type::Range, Type::Any),
26 ])
27 .optional(
28 "rows",
29 SyntaxShape::OneOf(vec![SyntaxShape::Int, SyntaxShape::Filesize]),
30 "Starting from the back, the number of rows to return.",
31 )
32 .allow_variants_without_examples(true)
33 .switch("strict", "Throw an error if input is empty.", Some('s'))
34 .category(Category::Filters)
35 }
36
37 fn search_terms(&self) -> Vec<&str> {
38 vec!["tail", "end"]
39 }
40
41 fn description(&self) -> &str {
42 "Return only the last several rows of the input. Counterpart of `first`. Opposite of `drop`. For binary input, rows can also be specified as a filesize."
43 }
44
45 fn examples(&self) -> Vec<Example<'_>> {
46 vec![
47 Example {
48 example: "[1,2,3] | last 2",
49 description: "Return the last 2 items of a list/table.",
50 result: Some(Value::list(
51 vec![Value::test_int(2), Value::test_int(3)],
52 Span::test_data(),
53 )),
54 },
55 Example {
56 example: "[1,2,3] | last",
57 description: "Return the last item of a list/table.",
58 result: Some(Value::test_int(3)),
59 },
60 Example {
61 example: "0x[01 23 45] | last 2",
62 description: "Return the last 2 bytes of a binary value.",
63 result: Some(Value::binary(vec![0x23, 0x45], Span::test_data())),
64 },
65 Example {
66 example: "1..3 | last",
67 description: "Return the last item of a range.",
68 result: Some(Value::test_int(3)),
69 },
70 Example {
71 example: "0x[01 23 45] | last 2b",
72 description: "Return the last 2 bytes of a binary value, using a filesize argument.",
73 result: Some(Value::test_binary(vec![0x23, 0x45])),
74 },
75 ]
76 }
77
78 fn run(
79 &self,
80 engine_state: &EngineState,
81 stack: &mut Stack,
82 call: &Call,
83 input: PipelineData,
84 ) -> Result<PipelineData, ShellError> {
85 let head = call.head;
86 let rows_val: Option<Value> = call.opt(engine_state, stack, 0)?;
87 let is_filesize = rows_val
88 .as_ref()
89 .is_some_and(|v| matches!(v, Value::Filesize { .. }));
90 let strict_mode = call.has_flag(engine_state, stack, "strict")?;
91
92 let rows: Option<usize> = match rows_val {
93 Some(v) => {
94 let span = v.span();
95 match v {
96 Value::Int { val, .. } => Some(
97 usize::try_from(val)
98 .map_err(|_| ShellError::NeedsPositiveValue { span })?,
99 ),
100 Value::Filesize { val, .. } => Some(
101 usize::try_from(val)
102 .map_err(|_| ShellError::NeedsPositiveValue { span })?,
103 ),
104 ref val => {
105 return Err(ShellError::RuntimeTypeMismatch {
106 expected: Type::custom("int or filesize"),
107 actual: val.get_type(),
108 span: val.span(),
109 });
110 }
111 }
112 }
113 None => None,
114 };
115
116 let return_single_element = rows.is_none();
119 let rows = rows.unwrap_or(1);
120
121 let mut input = input;
122 let metadata = input.take_metadata();
123
124 if is_filesize {
125 let is_binary = matches!(
126 &input,
127 PipelineData::Value(Value::Binary { .. }, _) | PipelineData::ByteStream(..)
128 );
129 if !is_binary {
130 return Err(ShellError::IncompatibleParametersSingle {
131 msg: "Filesize is only supported for binary/byte stream input".into(),
132 span: head,
133 });
134 }
135 }
136
137 if rows == 0 {
144 return match input {
145 PipelineData::Value(val, _) if matches!(&val, Value::Binary { .. }) => Ok(
146 Value::binary(Vec::new(), val.span()).into_pipeline_data_with_metadata(
147 metadata.map(|m| m.with_content_type(None)),
148 ),
149 ),
150 PipelineData::ByteStream(stream, _) => {
151 if stream.type_().is_binary_coercible() {
152 let span = stream.span();
153 Ok(
154 Value::binary(Vec::new(), span).into_pipeline_data_with_metadata(
155 metadata.map(|m| m.with_content_type(None)),
156 ),
157 )
158 } else {
159 Ok(
160 Value::list(Vec::new(), head)
161 .into_pipeline_data_with_metadata(metadata),
162 )
163 }
164 }
165 _ => Ok(Value::list(Vec::new(), head).into_pipeline_data_with_metadata(metadata)),
166 };
167 }
168
169 match input {
170 PipelineData::ListStream(_, _) | PipelineData::Value(Value::Range { .. }, _) => {
171 let iterator = input.into_iter_strict(head)?;
172
173 let mut buf = VecDeque::new();
175
176 for row in iterator {
177 engine_state.signals().check(&head)?;
178 if buf.len() == rows {
179 buf.pop_front();
180 }
181 buf.push_back(row);
182 }
183
184 if return_single_element {
185 if let Some(last) = buf.pop_back() {
186 Ok(last.into_pipeline_data_with_metadata(metadata))
187 } else if strict_mode {
188 Err(ShellError::AccessEmptyContent { span: head })
189 } else {
190 Ok(Value::nothing(head).into_pipeline_data_with_metadata(metadata))
193 }
194 } else {
195 Ok(Value::list(buf.into(), head).into_pipeline_data_with_metadata(metadata))
196 }
197 }
198 PipelineData::Value(val, _) => {
199 let span = val.span();
200 match val {
201 Value::List { vals, .. } => {
202 if return_single_element {
203 if let Some(v) = vals.last() {
204 Ok(v.clone().into_pipeline_data_with_metadata(metadata))
205 } else if strict_mode {
206 Err(ShellError::AccessEmptyContent { span: head })
207 } else {
208 Ok(Value::nothing(head).into_pipeline_data_with_metadata(metadata))
211 }
212 } else {
213 let i = vals.len().saturating_sub(rows);
214 let value = if i == 0 {
215 Value::list_shared(vals, span)
216 } else {
217 Value::list(vals.iter().skip(i).cloned().collect(), span)
218 };
219 Ok(value.into_pipeline_data_with_metadata(metadata))
220 }
221 }
222 Value::Binary { val, .. } => {
223 let binary_meta = metadata.map(|m| m.with_content_type(None));
224 if return_single_element {
225 if let Some(&val) = val.last() {
226 Ok(Value::int(val.into(), span)
227 .into_pipeline_data_with_metadata(binary_meta))
228 } else if strict_mode {
229 Err(ShellError::AccessEmptyContent { span: head })
230 } else {
231 Ok(Value::nothing(head)
234 .into_pipeline_data_with_metadata(binary_meta))
235 }
236 } else {
237 let mut val = val.into_owned();
238 let i = val.len().saturating_sub(rows);
239 val.drain(..i);
240 Ok(Value::binary(val, span)
241 .into_pipeline_data_with_metadata(binary_meta))
242 }
243 }
244 Value::Error { error, .. } => Err(*error),
246 #[cfg(feature = "sqlite")]
247 Value::Custom {
249 val: custom_val,
250 internal_span,
251 ..
252 } => {
253 if let Some(plan) = QueryPlan::try_from_any(custom_val.as_any()) {
254 if return_single_element {
255 let plan =
257 plan.with_order_by("rowid DESC".to_string()).with_limit(1);
258 let result = plan.execute(head)?;
259 let value = result.into_value(head)?;
260 if let Value::List { vals, .. } = value {
261 if let Some(val) = vals.into_iter().next() {
262 Ok(val.into_pipeline_data_with_metadata(metadata))
263 } else if strict_mode {
264 Err(ShellError::AccessEmptyContent { span: head })
265 } else {
266 Ok(Value::nothing(head)
269 .into_pipeline_data_with_metadata(metadata))
270 }
271 } else {
272 Err(ShellError::NushellFailed {
273 msg: "Expected list from query plan".into(),
274 })
275 }
276 } else {
277 let plan = plan
279 .with_order_by("rowid DESC".to_string())
280 .with_limit(rows as i64);
281 let result = plan.execute(head)?;
282 let value = result.into_value(head)?;
283
284 if let Value::List { mut vals, .. } = value {
285 vals.to_mut().reverse();
287 Ok(Value::list(vals.into_owned(), head)
288 .into_pipeline_data_with_metadata(metadata))
289 } else {
290 Ok(value.into_pipeline_data_with_metadata(metadata))
291 }
292 }
293 } else {
294 Err(ShellError::OnlySupportsThisInputType {
295 exp_input_type: "list, binary or range".into(),
296 wrong_type: custom_val.type_name(),
297 dst_span: head,
298 src_span: internal_span,
299 })
300 }
301 }
302 other => Err(ShellError::OnlySupportsThisInputType {
303 exp_input_type: "list, binary or range".into(),
304 wrong_type: other.get_type().to_string(),
305 dst_span: head,
306 src_span: other.span(),
307 }),
308 }
309 }
310 PipelineData::ByteStream(stream, ..) => {
311 if stream.type_().is_binary_coercible() {
312 let span = stream.span();
313 let byte_meta = metadata.map(|m| m.with_content_type(None));
314 if let Some(mut reader) = stream.reader() {
315 const TAKE: u64 = 8192;
318 let mut buf = VecDeque::with_capacity(rows + TAKE as usize);
319 loop {
320 let taken = std::io::copy(&mut (&mut reader).take(TAKE), &mut buf)
321 .map_err(|err| IoError::new(err, span, None))?;
322 if buf.len() > rows {
323 buf.drain(..(buf.len() - rows));
324 }
325 if taken < TAKE {
326 if return_single_element {
328 if !buf.is_empty() {
329 return Ok(Value::int(buf[0] as i64, head)
330 .into_pipeline_data_with_metadata(byte_meta));
331 } else if strict_mode {
332 return Err(ShellError::AccessEmptyContent { span: head });
333 } else {
334 return Ok(Value::nothing(head)
337 .into_pipeline_data_with_metadata(byte_meta));
338 }
339 } else {
340 return Ok(Value::binary(buf, head)
341 .into_pipeline_data_with_metadata(byte_meta));
342 }
343 }
344 }
345 } else {
346 Ok(Value::nothing(head).into_pipeline_data_with_metadata(byte_meta))
347 }
348 } else {
349 Err(ShellError::OnlySupportsThisInputType {
350 exp_input_type: "list, binary or range".into(),
351 wrong_type: stream.type_().describe().into(),
352 dst_span: head,
353 src_span: stream.span(),
354 })
355 }
356 }
357 PipelineData::Empty => Err(ShellError::OnlySupportsThisInputType {
358 exp_input_type: "list, binary or range".into(),
359 wrong_type: "null".into(),
360 dst_span: call.head,
361 src_span: call.head,
362 }),
363 }
364 }
365}
366
367#[cfg(test)]
368mod test {
369 use super::*;
370
371 #[test]
372 fn test_examples() -> nu_test_support::Result {
373 nu_test_support::test().examples(Last)
374 }
375}