vrl/stdlib/casing/
screamingsnakecase.rs

1use crate::compiler::prelude::*;
2
3use crate::stdlib::casing::{ORIGINAL_CASE, into_case};
4use convert_case::Case;
5
6#[derive(Clone, Copy, Debug)]
7pub struct ScreamingSnakecase;
8
9impl Function for ScreamingSnakecase {
10    fn identifier(&self) -> &'static str {
11        "screamingsnakecase"
12    }
13
14    fn usage(&self) -> &'static str {
15        "Takes the `value` string, and turns it into SCREAMING_SNAKE case. Optionally, you can pass in the existing case of the function, or else we will try to figure out the case automatically."
16    }
17
18    fn category(&self) -> &'static str {
19        Category::String.as_ref()
20    }
21
22    fn return_kind(&self) -> u16 {
23        kind::BYTES
24    }
25
26    fn parameters(&self) -> &'static [Parameter] {
27        const PARAMETERS: &[Parameter] = &[
28            Parameter::required(
29                "value",
30                kind::BYTES,
31                "The string to convert to SCREAMING_SNAKE case.",
32            ),
33            ORIGINAL_CASE,
34        ];
35
36        PARAMETERS
37    }
38
39    fn compile(
40        &self,
41        state: &state::TypeState,
42        _ctx: &mut FunctionCompileContext,
43        arguments: ArgumentList,
44    ) -> Compiled {
45        let value = arguments.required("value");
46        let original_case = arguments
47            .optional_enum("original_case", &super::variants(), state)?
48            .map(|b| {
49                into_case(
50                    b.try_bytes_utf8_lossy()
51                        .expect("cant convert to string")
52                        .as_ref(),
53                )
54            })
55            .transpose()?;
56
57        Ok(ScreamingSnakecaseFn {
58            value,
59            original_case,
60        }
61        .as_expr())
62    }
63
64    fn examples(&self) -> &'static [Example] {
65        &[
66            example! {
67                title: "SCREAMING_SNAKE_CASE a string without specifying original case",
68                source: r#"screamingsnakecase("input-string")"#,
69                result: Ok("INPUT_STRING"),
70            },
71            example! {
72                title: "SCREAMING_SNAKE_CASE a snake_case string",
73                source: r#"screamingsnakecase("foo_bar_baz", "snake_case")"#,
74                result: Ok("FOO_BAR_BAZ"),
75            },
76            example! {
77                title: "SCREAMING_SNAKE_CASE specifying the wrong original case (capitalizes but doesn't include `_` properly)",
78                source: r#"screamingsnakecase("FooBarBaz", "kebab-case")"#,
79                result: Ok("FOOBARBAZ"),
80            },
81        ]
82    }
83}
84
85#[derive(Debug, Clone)]
86struct ScreamingSnakecaseFn {
87    value: Box<dyn Expression>,
88    original_case: Option<Case>,
89}
90
91impl FunctionExpression for ScreamingSnakecaseFn {
92    fn resolve(&self, ctx: &mut Context) -> Resolved {
93        let value = self.value.resolve(ctx)?;
94        super::convert_case(&value, Case::Constant, self.original_case)
95    }
96
97    fn type_def(&self, _: &state::TypeState) -> TypeDef {
98        TypeDef::bytes().infallible()
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105    use crate::value;
106
107    test_function![
108        screamingsnakecase => ScreamingSnakecase;
109
110        simple {
111            args: func_args![value: value!("input_string"), original_case: "snake_case"],
112            want: Ok(value!("INPUT_STRING")),
113            tdef: TypeDef::bytes(),
114        }
115
116        no_case {
117            args: func_args![value: value!("input_string")],
118            want: Ok(value!("INPUT_STRING")),
119            tdef: TypeDef::bytes(),
120        }
121    ];
122}