vrl/stdlib/
parse_query_string.rs

1use crate::compiler::prelude::*;
2use crate::parsing::query_string::parse_query_string;
3
4#[derive(Clone, Copy, Debug)]
5pub struct ParseQueryString;
6
7impl Function for ParseQueryString {
8    fn identifier(&self) -> &'static str {
9        "parse_query_string"
10    }
11
12    fn usage(&self) -> &'static str {
13        "Parses the `value` as a query string."
14    }
15
16    fn category(&self) -> &'static str {
17        Category::Parse.as_ref()
18    }
19
20    fn return_kind(&self) -> u16 {
21        kind::OBJECT
22    }
23
24    fn notices(&self) -> &'static [&'static str] {
25        &[indoc! {"
26            All values are returned as strings. We recommend manually coercing values to desired
27            types as you see fit. Empty keys and values are allowed.
28        "}]
29    }
30
31    fn examples(&self) -> &'static [Example] {
32        &[
33            example! {
34                title: "Parse simple query string",
35                source: r#"parse_query_string("foo=1&bar=2")"#,
36                result: Ok(r#"
37                {
38                    "foo": "1",
39                    "bar": "2"
40                }
41            "#),
42            },
43            example! {
44                title: "Parse query string",
45                source: r#"parse_query_string("foo=%2B1&bar=2&bar=3&xyz")"#,
46                result: Ok(indoc! {r#"{
47                    "bar": [
48                        "2",
49                        "3"
50                    ],
51                    "foo": "+1",
52                    "xyz": ""
53                }"#}),
54            },
55            example! {
56                title: "Parse Ruby on Rails' query string",
57                source: r#"parse_query_string("?foo%5b%5d=1&foo%5b%5d=2")"#,
58                result: Ok(r#"{"foo[]": ["1", "2"]}"#),
59            },
60        ]
61    }
62
63    fn compile(
64        &self,
65        _state: &state::TypeState,
66        _ctx: &mut FunctionCompileContext,
67        arguments: ArgumentList,
68    ) -> Compiled {
69        let value = arguments.required("value");
70        Ok(ParseQueryStringFn { value }.as_expr())
71    }
72
73    fn parameters(&self) -> &'static [Parameter] {
74        const PARAMETERS: &[Parameter] = &[Parameter::required(
75            "value",
76            kind::BYTES,
77            "The string to parse.",
78        )];
79        PARAMETERS
80    }
81}
82
83#[derive(Debug, Clone)]
84struct ParseQueryStringFn {
85    value: Box<dyn Expression>,
86}
87
88impl FunctionExpression for ParseQueryStringFn {
89    fn resolve(&self, ctx: &mut Context) -> Resolved {
90        let bytes = self.value.resolve(ctx)?.try_bytes()?;
91        parse_query_string(&bytes, false)
92    }
93
94    fn type_def(&self, _: &state::TypeState) -> TypeDef {
95        TypeDef::object(inner_kind())
96    }
97}
98
99fn inner_kind() -> Collection<Field> {
100    Collection::from_unknown(Kind::bytes().or_array(Collection::any()))
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use crate::value;
107
108    test_function![
109        parse_query_string => ParseQueryString;
110
111        complete {
112            args: func_args![value: value!("foo=%2B1&bar=2&xyz=&abc")],
113            want: Ok(value!({
114                foo: "+1",
115                bar: "2",
116                xyz: "",
117                abc: "",
118            })),
119            tdef: TypeDef::object(inner_kind()),
120        }
121
122        multiple_values {
123            args: func_args![value: value!("foo=bar&foo=xyz")],
124            want: Ok(value!({
125                foo: ["bar", "xyz"],
126            })),
127            tdef: TypeDef::object(inner_kind()),
128        }
129
130        ruby_on_rails_multiple_values {
131            args: func_args![value: value!("?foo%5b%5d=bar&foo%5b%5d=xyz")],
132            want: Ok(value!({
133                "foo[]": ["bar", "xyz"],
134            })),
135            tdef: TypeDef::object(inner_kind()),
136        }
137
138        empty_key {
139            args: func_args![value: value!("=&=")],
140            want: Ok(value!({
141                "": ["", ""],
142            })),
143            tdef: TypeDef::object(inner_kind()),
144        }
145
146        single_key {
147            args: func_args![value: value!("foo")],
148            want: Ok(value!({
149                foo: "",
150            })),
151            tdef: TypeDef::object(inner_kind()),
152        }
153
154        empty {
155            args: func_args![value: value!("")],
156            want: Ok(value!({})),
157            tdef: TypeDef::object(inner_kind()),
158        }
159
160        starts_with_question_mark {
161            args: func_args![value: value!("?foo=bar")],
162            want: Ok(value!({
163                foo: "bar",
164            })),
165            tdef: TypeDef::object(inner_kind()),
166        }
167    ];
168}