vrl/stdlib/
decode_percent.rs

1use crate::compiler::prelude::*;
2use percent_encoding::percent_decode;
3
4fn decode_percent(value: Value) -> Resolved {
5    let value = value.try_bytes()?;
6    Ok(percent_decode(&value)
7        .decode_utf8_lossy()
8        .to_string()
9        .into())
10}
11
12#[derive(Clone, Copy, Debug)]
13pub struct DecodePercent;
14
15impl Function for DecodePercent {
16    fn identifier(&self) -> &'static str {
17        "decode_percent"
18    }
19
20    fn usage(&self) -> &'static str {
21        "Decodes a [percent-encoded](https://url.spec.whatwg.org/#percent-encoded-bytes) `value` like a URL."
22    }
23
24    fn category(&self) -> &'static str {
25        Category::Codec.as_ref()
26    }
27
28    fn return_kind(&self) -> u16 {
29        kind::BYTES
30    }
31
32    fn parameters(&self) -> &'static [Parameter] {
33        const PARAMETERS: &[Parameter] = &[Parameter::required(
34            "value",
35            kind::BYTES,
36            "The string to decode.",
37        )];
38        PARAMETERS
39    }
40
41    fn compile(
42        &self,
43        _state: &state::TypeState,
44        _ctx: &mut FunctionCompileContext,
45        arguments: ArgumentList,
46    ) -> Compiled {
47        let value = arguments.required("value");
48
49        Ok(DecodePercentFn { value }.as_expr())
50    }
51
52    fn examples(&self) -> &'static [Example] {
53        &[example! {
54            title: "Percent decode a value",
55            source: r#"decode_percent("foo%20bar%3F")"#,
56            result: Ok("foo bar?"),
57        }]
58    }
59}
60
61#[derive(Clone, Debug)]
62struct DecodePercentFn {
63    value: Box<dyn Expression>,
64}
65
66impl FunctionExpression for DecodePercentFn {
67    fn resolve(&self, ctx: &mut Context) -> Resolved {
68        let value = self.value.resolve(ctx)?;
69
70        decode_percent(value)
71    }
72
73    fn type_def(&self, _: &state::TypeState) -> TypeDef {
74        TypeDef::bytes().infallible()
75    }
76}
77
78#[cfg(test)]
79mod test {
80    use super::*;
81    use crate::value;
82
83    test_function![
84        decode_percent => DecodePercent;
85
86        decode {
87            args: func_args![value: value!("foo%20%23%22%3C%3E%3F%60%7B%7D%2F%3A%3B%3D%40%5B%5C%5D%5E%7C%24%25%26%2B%2C%21%27%28%29%7Ebar")],
88            want: Ok(value!(r#"foo #"<>?`{}/:;=@[\]^|$%&+,!'()~bar"#)),
89            tdef: TypeDef::bytes().infallible(),
90        }
91    ];
92}