vrl/stdlib/
uuid_from_friendly_id.rs

1use crate::compiler::prelude::*;
2use bytes::Bytes;
3
4fn uuid_from_friendly_id(value: &Value) -> Resolved {
5    let mut buf = [0; 36];
6    let value = value.try_bytes_utf8_lossy()?;
7    match base62::decode(value.as_ref()) {
8        Err(err) => Err(format!("failed to decode friendly id: {err}").into()),
9        Ok(w128) => {
10            let uuid = uuid::Uuid::from_u128(w128)
11                .hyphenated()
12                .encode_lower(&mut buf);
13            Ok(Bytes::copy_from_slice(uuid.as_bytes()).into())
14        }
15    }
16}
17
18#[derive(Clone, Copy, Debug)]
19pub struct UuidFromFriendlyId;
20
21impl Function for UuidFromFriendlyId {
22    fn identifier(&self) -> &'static str {
23        "uuid_from_friendly_id"
24    }
25
26    fn usage(&self) -> &'static str {
27        "Convert a Friendly ID (base62 encoding a 128-bit word) to a UUID."
28    }
29
30    fn category(&self) -> &'static str {
31        Category::Random.as_ref()
32    }
33
34    fn internal_failure_reasons(&self) -> &'static [&'static str] {
35        &[
36            "`value` is a string but the text uses characters outside of class [0-9A-Za-z].",
37            "`value` is a base62 encoding of an integer, but the integer is greater than or equal to 2^128.",
38        ]
39    }
40
41    fn return_kind(&self) -> u16 {
42        kind::BYTES
43    }
44
45    fn parameters(&self) -> &'static [Parameter] {
46        const PARAMETERS: &[Parameter] = &[Parameter::required(
47            "value",
48            kind::BYTES,
49            "A string that is a Friendly ID",
50        )];
51        PARAMETERS
52    }
53
54    fn examples(&self) -> &'static [Example] {
55        &[example! {
56            title: "Convert a Friendly ID to a UUID",
57            source: r#"uuid_from_friendly_id!("3s87yEvnmkiPBMHsj8bwwc")"#,
58            result: Ok("7f41deed-d5e2-8b5e-7a13-ab4ff93cfad2"),
59        }]
60    }
61
62    fn compile(
63        &self,
64        _state: &state::TypeState,
65        _ctx: &mut FunctionCompileContext,
66        arguments: ArgumentList,
67    ) -> Compiled {
68        let value = arguments.required("value");
69        Ok(UuidFromFriendlyIdFn { value }.as_expr())
70    }
71}
72
73#[derive(Debug, Clone)]
74struct UuidFromFriendlyIdFn {
75    value: Box<dyn Expression>,
76}
77
78impl FunctionExpression for UuidFromFriendlyIdFn {
79    fn resolve(&self, ctx: &mut Context) -> Resolved {
80        let value = self.value.resolve(ctx)?;
81        uuid_from_friendly_id(&value)
82    }
83
84    fn type_def(&self, _: &TypeState) -> TypeDef {
85        TypeDef::bytes().fallible()
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use crate::value;
93
94    test_function![
95        uuid_from_friendly_id => UuidFromFriendlyId;
96        example_from_docs {
97            args: func_args![value: value!("3s87yEvnmkiPBMHsj8bwwc")],
98            want: Ok(value!("7f41deed-d5e2-8b5e-7a13-ab4ff93cfad2")),
99            tdef: TypeDef::bytes().fallible(),
100        }
101    ];
102}