vrl/stdlib/
length.rs

1use crate::compiler::prelude::*;
2
3fn length(value: Value) -> Resolved {
4    match value {
5        Value::Array(v) => Ok(v.len().into()),
6        Value::Object(v) => Ok(v.len().into()),
7        Value::Bytes(v) => Ok(v.len().into()),
8        value => Err(ValueError::Expected {
9            got: value.kind(),
10            expected: Kind::array(Collection::any())
11                | Kind::object(Collection::any())
12                | Kind::bytes(),
13        }
14        .into()),
15    }
16}
17
18#[derive(Clone, Copy, Debug)]
19pub struct Length;
20
21impl Function for Length {
22    fn identifier(&self) -> &'static str {
23        "length"
24    }
25
26    fn usage(&self) -> &'static str {
27        indoc! {"
28            Returns the length of the `value`.
29
30            * If `value` is an array, returns the number of elements.
31            * If `value` is an object, returns the number of top-level keys.
32            * If `value` is a string, returns the number of bytes in the string. If
33              you want the number of characters, see `strlen`.
34        "}
35    }
36
37    fn category(&self) -> &'static str {
38        Category::Enumerate.as_ref()
39    }
40
41    fn return_kind(&self) -> u16 {
42        kind::INTEGER
43    }
44
45    fn return_rules(&self) -> &'static [&'static str] {
46        &[
47            "If `value` is an array, returns the number of elements.",
48            "If `value` is an object, returns the number of top-level keys.",
49            "If `value` is a string, returns the number of bytes in the string.",
50        ]
51    }
52
53    fn parameters(&self) -> &'static [Parameter] {
54        const PARAMETERS: &[Parameter] = &[Parameter::required(
55            "value",
56            kind::ARRAY | kind::OBJECT | kind::BYTES,
57            "The array or object.",
58        )];
59        PARAMETERS
60    }
61
62    fn examples(&self) -> &'static [Example] {
63        &[
64            example! {
65                title: "Length (object)",
66                source: indoc! {r#"
67                    length({
68                        "portland": "Trail Blazers",
69                        "seattle": "Supersonics"
70                    })
71                "#},
72                result: Ok("2"),
73            },
74            example! {
75                title: "Length (nested object)",
76                source: indoc! {r#"
77                    length({
78                        "home": {
79                            "city":  "Portland",
80                            "state": "Oregon"
81                        },
82                        "name": "Trail Blazers",
83                        "mascot": {
84                            "name": "Blaze the Trail Cat"
85                        }
86                    })
87                "#},
88                result: Ok("3"),
89            },
90            example! {
91                title: "Length (array)",
92                source: r#"length(["Trail Blazers", "Supersonics", "Grizzlies"])"#,
93                result: Ok("3"),
94            },
95            example! {
96                title: "Length (string)",
97                source: r#"length("The Planet of the Apes Musical")"#,
98                result: Ok("30"),
99            },
100        ]
101    }
102
103    fn compile(
104        &self,
105        _state: &state::TypeState,
106        _ctx: &mut FunctionCompileContext,
107        arguments: ArgumentList,
108    ) -> Compiled {
109        let value = arguments.required("value");
110
111        Ok(LengthFn { value }.as_expr())
112    }
113}
114
115#[derive(Debug, Clone)]
116struct LengthFn {
117    value: Box<dyn Expression>,
118}
119
120impl FunctionExpression for LengthFn {
121    fn resolve(&self, ctx: &mut Context) -> Resolved {
122        let value = self.value.resolve(ctx)?;
123
124        length(value)
125    }
126
127    fn type_def(&self, _: &state::TypeState) -> TypeDef {
128        TypeDef::integer().infallible()
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use crate::value;
136
137    test_function![
138        length => Length;
139
140        non_empty_object_value {
141            args: func_args![value: value!({"foo": "bar", "baz": true, "baq": [1, 2, 3]})],
142            want: Ok(value!(3)),
143            tdef: TypeDef::integer().infallible(),
144        }
145
146        empty_object_value {
147            args: func_args![value: value!({})],
148            want: Ok(value!(0)),
149            tdef: TypeDef::integer().infallible(),
150        }
151
152        nested_object_value {
153            args: func_args![value: value!({"nested": {"foo": "bar"}})],
154            want: Ok(value!(1)),
155            tdef: TypeDef::integer().infallible(),
156        }
157
158        non_empty_array_value {
159            args: func_args![value: value!([1, 2, 3, 4, true, "hello"])],
160            want: Ok(value!(6)),
161            tdef: TypeDef::integer().infallible(),
162        }
163
164        empty_array_value {
165            args: func_args![value: value!([])],
166            want: Ok(value!(0)),
167            tdef: TypeDef::integer().infallible(),
168        }
169
170        string_value {
171            args: func_args![value: value!("hello world")],
172            want: Ok(value!(11)),
173            tdef: TypeDef::integer().infallible(),
174        }
175    ];
176}