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