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 = call.opt::<usize>(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 = rows.unwrap_or(1);
89
90 let mut input = input;
91 let metadata = input.take_metadata();
92
93 if rows == 0 {
100 return match input {
101 PipelineData::Value(val, _) if matches!(&val, Value::Binary { .. }) => Ok(
102 Value::binary(Vec::new(), val.span()).into_pipeline_data_with_metadata(
103 metadata.map(|m| m.with_content_type(None)),
104 ),
105 ),
106 PipelineData::ByteStream(stream, _) => {
107 if stream.type_().is_binary_coercible() {
108 let span = stream.span();
109 Ok(
110 Value::binary(Vec::new(), span).into_pipeline_data_with_metadata(
111 metadata.map(|m| m.with_content_type(None)),
112 ),
113 )
114 } else {
115 Ok(
116 Value::list(Vec::new(), head)
117 .into_pipeline_data_with_metadata(metadata),
118 )
119 }
120 }
121 _ => Ok(Value::list(Vec::new(), head).into_pipeline_data_with_metadata(metadata)),
122 };
123 }
124
125 match input {
126 PipelineData::ListStream(_, _) | PipelineData::Value(Value::Range { .. }, _) => {
127 let iterator = input.into_iter_strict(head)?;
128
129 let mut buf = VecDeque::new();
131
132 for row in iterator {
133 engine_state.signals().check(&head)?;
134 if buf.len() == rows {
135 buf.pop_front();
136 }
137 buf.push_back(row);
138 }
139
140 if return_single_element {
141 if let Some(last) = buf.pop_back() {
142 Ok(last.into_pipeline_data_with_metadata(metadata))
143 } else if strict_mode {
144 Err(ShellError::AccessEmptyContent { span: head })
145 } else {
146 Ok(Value::nothing(head).into_pipeline_data_with_metadata(metadata))
149 }
150 } else {
151 Ok(Value::list(buf.into(), head).into_pipeline_data_with_metadata(metadata))
152 }
153 }
154 PipelineData::Value(val, _) => {
155 let span = val.span();
156 match val {
157 Value::List { mut vals, .. } => {
158 if return_single_element {
159 if let Some(v) = vals.pop() {
160 Ok(v.into_pipeline_data_with_metadata(metadata))
161 } else if strict_mode {
162 Err(ShellError::AccessEmptyContent { span: head })
163 } else {
164 Ok(Value::nothing(head).into_pipeline_data_with_metadata(metadata))
167 }
168 } else {
169 let i = vals.len().saturating_sub(rows);
170 vals.drain(..i);
171 Ok(Value::list(vals, span).into_pipeline_data_with_metadata(metadata))
172 }
173 }
174 Value::Binary { mut val, .. } => {
175 let binary_meta = metadata.map(|m| m.with_content_type(None));
176 if return_single_element {
177 if let Some(val) = val.pop() {
178 Ok(Value::int(val.into(), span)
179 .into_pipeline_data_with_metadata(binary_meta))
180 } else if strict_mode {
181 Err(ShellError::AccessEmptyContent { span: head })
182 } else {
183 Ok(Value::nothing(head)
186 .into_pipeline_data_with_metadata(binary_meta))
187 }
188 } else {
189 let i = val.len().saturating_sub(rows);
190 val.drain(..i);
191 Ok(Value::binary(val, span)
192 .into_pipeline_data_with_metadata(binary_meta))
193 }
194 }
195 Value::Error { error, .. } => Err(*error),
197 #[cfg(feature = "sqlite")]
198 Value::Custom {
200 val: custom_val,
201 internal_span,
202 ..
203 } => {
204 if let Some(table) =
205 custom_val.as_any().downcast_ref::<SQLiteQueryBuilder>()
206 {
207 if return_single_element {
208 let new_table = table
210 .clone()
211 .with_order_by("rowid DESC".to_string())
212 .with_limit(1);
213 let result = new_table.execute(head)?;
214 let value = result.into_value(head)?;
215 if let Value::List { vals, .. } = value {
216 if let Some(val) = vals.into_iter().next() {
217 Ok(val.into_pipeline_data_with_metadata(metadata))
218 } else if strict_mode {
219 Err(ShellError::AccessEmptyContent { span: head })
220 } else {
221 Ok(Value::nothing(head)
224 .into_pipeline_data_with_metadata(metadata))
225 }
226 } else {
227 Err(ShellError::NushellFailed {
228 msg: "Expected list from SQLiteQueryBuilder".into(),
229 })
230 }
231 } else {
232 let new_table = table
234 .clone()
235 .with_order_by("rowid DESC".to_string())
236 .with_limit(rows as i64);
237 let result = new_table.execute(head)?;
238 let value = result.into_value(head)?;
239
240 if let Value::List { mut vals, .. } = value {
241 vals.reverse();
243 Ok(Value::list(vals, head)
244 .into_pipeline_data_with_metadata(metadata))
245 } else {
246 Ok(value.into_pipeline_data_with_metadata(metadata))
247 }
248 }
249 } else {
250 Err(ShellError::OnlySupportsThisInputType {
251 exp_input_type: "list, binary or range".into(),
252 wrong_type: custom_val.type_name(),
253 dst_span: head,
254 src_span: internal_span,
255 })
256 }
257 }
258 other => Err(ShellError::OnlySupportsThisInputType {
259 exp_input_type: "list, binary or range".into(),
260 wrong_type: other.get_type().to_string(),
261 dst_span: head,
262 src_span: other.span(),
263 }),
264 }
265 }
266 PipelineData::ByteStream(stream, ..) => {
267 if stream.type_().is_binary_coercible() {
268 let span = stream.span();
269 let byte_meta = metadata.map(|m| m.with_content_type(None));
270 if let Some(mut reader) = stream.reader() {
271 const TAKE: u64 = 8192;
274 let mut buf = VecDeque::with_capacity(rows + TAKE as usize);
275 loop {
276 let taken = std::io::copy(&mut (&mut reader).take(TAKE), &mut buf)
277 .map_err(|err| IoError::new(err, span, None))?;
278 if buf.len() > rows {
279 buf.drain(..(buf.len() - rows));
280 }
281 if taken < TAKE {
282 if return_single_element {
284 if !buf.is_empty() {
285 return Ok(Value::int(buf[0] as i64, head)
286 .into_pipeline_data_with_metadata(byte_meta));
287 } else if strict_mode {
288 return Err(ShellError::AccessEmptyContent { span: head });
289 } else {
290 return Ok(Value::nothing(head)
293 .into_pipeline_data_with_metadata(byte_meta));
294 }
295 } else {
296 return Ok(Value::binary(buf, head)
297 .into_pipeline_data_with_metadata(byte_meta));
298 }
299 }
300 }
301 } else {
302 Ok(Value::nothing(head).into_pipeline_data_with_metadata(byte_meta))
303 }
304 } else {
305 Err(ShellError::OnlySupportsThisInputType {
306 exp_input_type: "list, binary or range".into(),
307 wrong_type: stream.type_().describe().into(),
308 dst_span: head,
309 src_span: stream.span(),
310 })
311 }
312 }
313 PipelineData::Empty => Err(ShellError::OnlySupportsThisInputType {
314 exp_input_type: "list, binary or range".into(),
315 wrong_type: "null".into(),
316 dst_span: call.head,
317 src_span: call.head,
318 }),
319 }
320 }
321}
322
323#[cfg(test)]
324mod test {
325 use super::*;
326
327 #[test]
328 fn test_examples() -> nu_test_support::Result {
329 nu_test_support::test().examples(Last)
330 }
331}