nu_command/filters/
length.rs1#[cfg(feature = "sqlite")]
2use crate::database::QueryPlan;
3use nu_engine::command_prelude::*;
4use std::io::Read;
5
6#[derive(Clone)]
7pub struct Length;
8
9impl Command for Length {
10 fn name(&self) -> &str {
11 "length"
12 }
13
14 fn description(&self) -> &str {
15 "Count the number of items in an input list, rows in a table, or bytes in binary data."
16 }
17
18 fn signature(&self) -> nu_protocol::Signature {
19 Signature::build("length")
20 .input_output_types(vec![
21 (Type::List(Box::new(Type::Any)), Type::Int),
22 (Type::Binary, Type::Int),
23 (Type::Nothing, Type::Int),
24 #[cfg(feature = "sqlite")]
25 (Type::Custom("SQLiteQueryBuilder".into()), Type::Int),
26 ])
27 .allow_variants_without_examples(true)
28 .category(Category::Filters)
29 }
30
31 fn search_terms(&self) -> Vec<&str> {
32 vec!["count", "size", "wc"]
33 }
34
35 fn run(
36 &self,
37 _engine_state: &EngineState,
38 _stack: &mut Stack,
39 call: &Call,
40 input: PipelineData,
41 ) -> Result<PipelineData, ShellError> {
42 length_row(call, input)
43 }
44
45 fn examples(&self) -> Vec<Example<'_>> {
46 vec![
47 Example {
48 description: "Count the number of items in a list",
49 example: "[1 2 3 4 5] | length",
50 result: Some(Value::test_int(5)),
51 },
52 Example {
53 description: "Count the number of rows in a table",
54 example: "[{a:1 b:2}, {a:2 b:3}] | length",
55 result: Some(Value::test_int(2)),
56 },
57 Example {
58 description: "Count the number of bytes in binary data",
59 example: "0x[01 02] | length",
60 result: Some(Value::test_int(2)),
61 },
62 Example {
63 description: "Count the length a null value",
64 example: "null | length",
65 result: Some(Value::test_int(0)),
66 },
67 ]
68 }
69}
70
71fn length_row(call: &Call, input: PipelineData) -> Result<PipelineData, ShellError> {
72 let span = input.span().unwrap_or(call.head);
73
74 #[cfg(feature = "sqlite")]
75 if let PipelineData::Value(Value::Custom { val, .. }, ..) = &input
77 && let Some(plan) = QueryPlan::try_from_any(val.as_any())
78 {
79 let count = plan.count(call.head)?;
80 return Ok(Value::int(count, call.head).into_pipeline_data());
81 }
82
83 match input {
84 PipelineData::Empty | PipelineData::Value(Value::Nothing { .. }, ..) => {
85 Ok(Value::int(0, call.head).into_pipeline_data())
86 }
87 PipelineData::Value(Value::Binary { val, .. }, ..) => {
88 Ok(Value::int(val.len() as i64, call.head).into_pipeline_data())
89 }
90 #[cfg(feature = "sqlite")]
91 PipelineData::Value(
92 Value::Custom {
93 val, internal_span, ..
94 },
95 ..,
96 ) => Err(ShellError::OnlySupportsThisInputType {
97 exp_input_type: "list, table, binary, and nothing".into(),
98 wrong_type: val.type_name(),
99 dst_span: call.head,
100 src_span: internal_span,
101 }),
102 PipelineData::Value(Value::List { vals, .. }, ..) => {
103 Ok(Value::int(vals.len() as i64, call.head).into_pipeline_data())
104 }
105 PipelineData::ListStream(stream, ..) => {
106 Ok(Value::int(stream.into_iter().count() as i64, call.head).into_pipeline_data())
107 }
108 PipelineData::ByteStream(stream, ..) if stream.type_().is_binary_coercible() => {
109 Ok(Value::int(
110 match stream.reader() {
111 Some(r) => r.bytes().count() as i64,
112 None => 0,
113 },
114 call.head,
115 )
116 .into_pipeline_data())
117 }
118 _ => Err(ShellError::OnlySupportsThisInputType {
119 exp_input_type: "list, table, binary, and nothing".into(),
120 wrong_type: input.get_type().to_string(),
121 dst_span: call.head,
122 src_span: span,
123 }),
124 }
125}
126
127#[cfg(test)]
128mod test {
129 use super::*;
130
131 #[test]
132 fn test_examples() -> nu_test_support::Result {
133 nu_test_support::test().examples(Length)
134 }
135}