vrl/stdlib/
encode_lz4.rs

1use crate::compiler::prelude::*;
2use lz4_flex::block::{compress, compress_prepend_size};
3use nom::AsBytes;
4use std::sync::LazyLock;
5
6static DEFAULT_PREPEND_SIZE: LazyLock<Value> = LazyLock::new(|| Value::Boolean(true));
7
8static PARAMETERS: LazyLock<Vec<Parameter>> = LazyLock::new(|| {
9    vec![
10        Parameter::required("value", kind::BYTES, "The string to encode."),
11        Parameter::optional(
12            "prepend_size",
13            kind::BOOLEAN,
14            "Whether to prepend the original size to the compressed data.",
15        )
16        .default(&DEFAULT_PREPEND_SIZE),
17    ]
18});
19
20fn encode_lz4(value: Value, prepend_size: bool) -> Resolved {
21    let value = value.try_bytes()?;
22    if prepend_size {
23        let encoded = compress_prepend_size(value.as_bytes());
24        return Ok(Value::Bytes(encoded.into()));
25    }
26    let encoded = compress(value.as_bytes());
27    Ok(Value::Bytes(encoded.into()))
28}
29
30#[derive(Clone, Copy, Debug)]
31pub struct EncodeLz4;
32
33impl Function for EncodeLz4 {
34    fn identifier(&self) -> &'static str {
35        "encode_lz4"
36    }
37
38    fn usage(&self) -> &'static str {
39        indoc! {"
40            Encodes the `value` to [Lz4](https://lz4.github.io/lz4/). This function compresses the
41            input string into an lz4 block. If `prepend_size` is set to `true`, it prepends the
42            original uncompressed size to the compressed data. This is useful for some
43            implementations of lz4 that require the original size to be known before decoding.
44        "}
45    }
46
47    fn category(&self) -> &'static str {
48        Category::Codec.as_ref()
49    }
50
51    fn return_kind(&self) -> u16 {
52        kind::BYTES
53    }
54
55    fn examples(&self) -> &'static [Example] {
56        &[example! {
57            title: "Encode to Lz4",
58            source: r#"encode_base64(encode_lz4!("The quick brown fox jumps over 13 lazy dogs.", true))"#,
59            result: Ok("LAAAAPAdVGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy4="),
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        let prepend_size = arguments.optional("prepend_size");
71
72        Ok(EncodeLz4Fn {
73            value,
74            prepend_size,
75        }
76        .as_expr())
77    }
78
79    fn parameters(&self) -> &'static [Parameter] {
80        PARAMETERS.as_slice()
81    }
82}
83
84#[derive(Clone, Debug)]
85struct EncodeLz4Fn {
86    value: Box<dyn Expression>,
87    prepend_size: Option<Box<dyn Expression>>,
88}
89
90impl FunctionExpression for EncodeLz4Fn {
91    fn resolve(&self, ctx: &mut Context) -> Resolved {
92        let value = self.value.resolve(ctx)?;
93        let prepend_size = self
94            .prepend_size
95            .map_resolve_with_default(ctx, || DEFAULT_PREPEND_SIZE.clone())?
96            .try_boolean()?;
97
98        encode_lz4(value, prepend_size)
99    }
100
101    fn type_def(&self, _state: &state::TypeState) -> TypeDef {
102        // Always fallible due to the possibility of decoding errors that VRL can't detect
103        TypeDef::bytes().fallible()
104    }
105}
106
107#[cfg(test)]
108mod test {
109    use super::*;
110    use crate::value;
111    use nom::AsBytes;
112
113    fn decode_base64(text: &str) -> Vec<u8> {
114        base64_simd::STANDARD
115            .decode_to_vec(text)
116            .expect("Cannot decode from Base64")
117    }
118
119    test_function![
120        encode_lz4 => EncodeLz4;
121
122        success {
123            args: func_args![value: value!("The quick brown fox jumps over 13 lazy dogs."), prepend_size: value!(true)],
124            want: Ok(value!(decode_base64("LAAAAPAdVGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy4=").as_bytes())),
125            tdef: TypeDef::bytes().fallible(),
126        }
127    ];
128}