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 parameters(&self) -> &'static [Parameter] {
38        &[Parameter {
39            keyword: "value",
40            kind: kind::ARRAY | kind::OBJECT | kind::BYTES,
41            required: true,
42        }]
43    }
44
45    fn examples(&self) -> &'static [Example] {
46        &[
47            example! {
48                title: "Length (object)",
49                source: r#"length({ "portland": "Trail Blazers", "seattle": "Supersonics" })"#,
50                result: Ok("2"),
51            },
52            example! {
53                title: "Length (nested object)",
54                source: r#"length({ "home": { "city": "Portland", "state": "Oregon" }, "name": "Trail Blazers", "mascot": { "name": "Blaze the Trail Cat" } })"#,
55                result: Ok("3"),
56            },
57            example! {
58                title: "Length (array)",
59                source: r#"length(["Trail Blazers", "Supersonics", "Grizzlies"])"#,
60                result: Ok("3"),
61            },
62            example! {
63                title: "Length (string)",
64                source: r#"length("The Planet of the Apes Musical")"#,
65                result: Ok("30"),
66            },
67        ]
68    }
69
70    fn compile(
71        &self,
72        _state: &state::TypeState,
73        _ctx: &mut FunctionCompileContext,
74        arguments: ArgumentList,
75    ) -> Compiled {
76        let value = arguments.required("value");
77
78        Ok(LengthFn { value }.as_expr())
79    }
80}
81
82#[derive(Debug, Clone)]
83struct LengthFn {
84    value: Box<dyn Expression>,
85}
86
87impl FunctionExpression for LengthFn {
88    fn resolve(&self, ctx: &mut Context) -> Resolved {
89        let value = self.value.resolve(ctx)?;
90
91        length(value)
92    }
93
94    fn type_def(&self, _: &state::TypeState) -> TypeDef {
95        TypeDef::integer().infallible()
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use crate::value;
103
104    test_function![
105        length => Length;
106
107        non_empty_object_value {
108            args: func_args![value: value!({"foo": "bar", "baz": true, "baq": [1, 2, 3]})],
109            want: Ok(value!(3)),
110            tdef: TypeDef::integer().infallible(),
111        }
112
113        empty_object_value {
114            args: func_args![value: value!({})],
115            want: Ok(value!(0)),
116            tdef: TypeDef::integer().infallible(),
117        }
118
119        nested_object_value {
120            args: func_args![value: value!({"nested": {"foo": "bar"}})],
121            want: Ok(value!(1)),
122            tdef: TypeDef::integer().infallible(),
123        }
124
125        non_empty_array_value {
126            args: func_args![value: value!([1, 2, 3, 4, true, "hello"])],
127            want: Ok(value!(6)),
128            tdef: TypeDef::integer().infallible(),
129        }
130
131        empty_array_value {
132            args: func_args![value: value!([])],
133            want: Ok(value!(0)),
134            tdef: TypeDef::integer().infallible(),
135        }
136
137        string_value {
138            args: func_args![value: value!("hello world")],
139            want: Ok(value!(11)),
140            tdef: TypeDef::integer().infallible(),
141        }
142    ];
143}