vrl/stdlib/
encode_json.rs

1use crate::compiler::prelude::*;
2use std::sync::LazyLock;
3
4static DEFAULT_PRETTY: LazyLock<Value> = LazyLock::new(|| Value::Boolean(false));
5
6static PARAMETERS: LazyLock<Vec<Parameter>> = LazyLock::new(|| {
7    vec![
8        Parameter::required("value", kind::ANY, "The value to convert to a JSON string."),
9        Parameter::optional(
10            "pretty",
11            kind::BOOLEAN,
12            "Whether to pretty print the JSON string or not.",
13        )
14        .default(&DEFAULT_PRETTY),
15    ]
16});
17
18fn encode_json(value: &Value, pretty: bool) -> Value {
19    // With `vrl::Value` it should not be possible to get `Err`.
20
21    let result = if pretty {
22        serde_json::to_string_pretty(&value)
23    } else {
24        serde_json::to_string(&value)
25    };
26
27    match result {
28        Ok(value) => value.into(),
29        Err(error) => unreachable!("unable encode to json: {}", error),
30    }
31}
32
33#[derive(Clone, Copy, Debug)]
34pub struct EncodeJson;
35
36impl Function for EncodeJson {
37    fn identifier(&self) -> &'static str {
38        "encode_json"
39    }
40
41    fn usage(&self) -> &'static str {
42        "Encodes the `value` to JSON."
43    }
44
45    fn category(&self) -> &'static str {
46        Category::Codec.as_ref()
47    }
48
49    fn return_kind(&self) -> u16 {
50        kind::BYTES
51    }
52
53    fn parameters(&self) -> &'static [Parameter] {
54        PARAMETERS.as_slice()
55    }
56
57    fn compile(
58        &self,
59        _state: &state::TypeState,
60        _ctx: &mut FunctionCompileContext,
61        arguments: ArgumentList,
62    ) -> Compiled {
63        let value = arguments.required("value");
64        let pretty = arguments.optional("pretty");
65
66        Ok(EncodeJsonFn { value, pretty }.as_expr())
67    }
68
69    fn examples(&self) -> &'static [Example] {
70        &[
71            example! {
72                title: "Encode object to JSON",
73                source: r#"encode_json({"field": "value", "another": [1,2,3]})"#,
74                result: Ok(r#"s'{"another":[1,2,3],"field":"value"}'"#),
75            },
76            example! {
77                title: "Encode object to as pretty-printed JSON",
78                source: r#"encode_json({"field": "value", "another": [1,2,3]}, true)"#,
79                result: Ok(
80                    r#""{\n  \"another\": [\n    1,\n    2,\n    3\n  ],\n  \"field\": \"value\"\n}""#,
81                ),
82            },
83        ]
84    }
85}
86
87#[derive(Clone, Debug)]
88struct EncodeJsonFn {
89    value: Box<dyn Expression>,
90    pretty: Option<Box<dyn Expression>>,
91}
92
93impl FunctionExpression for EncodeJsonFn {
94    fn resolve(&self, ctx: &mut Context) -> Resolved {
95        let value = self.value.resolve(ctx)?;
96        let pretty = self
97            .pretty
98            .map_resolve_with_default(ctx, || DEFAULT_PRETTY.clone())?
99            .try_boolean()?;
100        Ok(encode_json(&value, pretty))
101    }
102
103    fn type_def(&self, _: &state::TypeState) -> TypeDef {
104        TypeDef::bytes().infallible()
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    use chrono::{DateTime, Utc};
113    use regex::Regex;
114
115    test_function![
116        encode_json => EncodeJson;
117
118        bytes {
119            args: func_args![value: "hello"],
120            want: Ok(r#""hello""#),
121            tdef: TypeDef::bytes().infallible(),
122        }
123
124        bytes_pretty {
125            args: func_args![value: "hello", pretty: true],
126            want: Ok(r#""hello""#),
127            tdef: TypeDef::bytes().infallible(),
128        }
129
130        integer {
131            args: func_args![value: 42],
132            want: Ok("42"),
133            tdef: TypeDef::bytes().infallible(),
134        }
135
136        integer_pretty {
137            args: func_args![value: 42, pretty: true],
138            want: Ok("42"),
139            tdef: TypeDef::bytes().infallible(),
140        }
141
142        float {
143            args: func_args![value: 42f64],
144            want: Ok("42.0"),
145            tdef: TypeDef::bytes().infallible(),
146        }
147
148        float_pretty {
149            args: func_args![value: 42f64, pretty: true],
150            want: Ok("42.0"),
151            tdef: TypeDef::bytes().infallible(),
152        }
153
154        boolean {
155            args: func_args![value: false],
156            want: Ok("false"),
157            tdef: TypeDef::bytes().infallible(),
158        }
159
160        boolean_pretty {
161            args: func_args![value: false, pretty: true],
162            want: Ok("false"),
163            tdef: TypeDef::bytes().infallible(),
164        }
165
166        map {
167            args: func_args![value: Value::from_iter([(String::from("field"), Value::from("value"))])],
168            want: Ok(r#"{"field":"value"}"#),
169            tdef: TypeDef::bytes().infallible(),
170        }
171
172        map_pretty {
173            args: func_args![value: Value::from_iter([(String::from("field"), Value::from("value"))]), pretty: true],
174            want: Ok("{\n  \"field\": \"value\"\n}"),
175            tdef: TypeDef::bytes().infallible(),
176        }
177
178        array {
179            args: func_args![value: vec![1, 2, 3]],
180            want: Ok("[1,2,3]"),
181            tdef: TypeDef::bytes().infallible(),
182        }
183
184        array_pretty {
185            args: func_args![value: vec![1, 2, 3], pretty: true],
186            want: Ok("[\n  1,\n  2,\n  3\n]"),
187            tdef: TypeDef::bytes().infallible(),
188        }
189
190        timestamp {
191            args: func_args![
192                value: DateTime::parse_from_str("1983 Apr 13 12:09:14.274 +0000", "%Y %b %d %H:%M:%S%.3f %z")
193                    .unwrap()
194                    .with_timezone(&Utc)
195            ],
196            want: Ok(r#""1983-04-13T12:09:14.274Z""#),
197            tdef: TypeDef::bytes().infallible(),
198        }
199
200        timestamp_pretty {
201            args: func_args![
202                value: DateTime::parse_from_str("1983 Apr 13 12:09:14.274 +0000", "%Y %b %d %H:%M:%S%.3f %z")
203                    .unwrap()
204                    .with_timezone(&Utc),
205                pretty: true
206            ],
207            want: Ok(r#""1983-04-13T12:09:14.274Z""#),
208            tdef: TypeDef::bytes().infallible(),
209        }
210
211        regex {
212            args: func_args![value: Regex::new("^a\\d+$").unwrap()],
213            want: Ok(r#""^a\\d+$""#),
214            tdef: TypeDef::bytes().infallible(),
215        }
216
217        regex_pretty {
218            args: func_args![value: Regex::new("^a\\d+$").unwrap(), pretty: true],
219            want: Ok(r#""^a\\d+$""#),
220            tdef: TypeDef::bytes().infallible(),
221        }
222
223        null {
224            args: func_args![value: Value::Null],
225            want: Ok("null"),
226            tdef: TypeDef::bytes().infallible(),
227        }
228
229        null_pretty {
230            args: func_args![value: Value::Null, pretty: true],
231            want: Ok("null"),
232            tdef: TypeDef::bytes().infallible(),
233        }
234    ];
235}