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 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}