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