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