vrl/stdlib/
to_regex.rs

1use crate::compiler::prelude::*;
2use tracing::warn;
3
4fn to_regex(value: &Value) -> Resolved {
5    let string = value.try_bytes_utf8_lossy()?;
6    let regex = regex::Regex::new(string.as_ref())
7        .map_err(|err| format!("could not create regex: {err}"))
8        .map(Into::into)?;
9    Ok(regex)
10}
11
12#[derive(Clone, Copy, Debug)]
13pub struct ToRegex;
14
15impl Function for ToRegex {
16    fn identifier(&self) -> &'static str {
17        "to_regex"
18    }
19
20    fn usage(&self) -> &'static str {
21        "Coerces the `value` into a regex."
22    }
23
24    fn category(&self) -> &'static str {
25        Category::Coerce.as_ref()
26    }
27
28    fn internal_failure_reasons(&self) -> &'static [&'static str] {
29        &["`value` is not a string."]
30    }
31
32    fn return_kind(&self) -> u16 {
33        kind::REGEX
34    }
35
36    fn return_rules(&self) -> &'static [&'static str] {
37        &[
38            "If `value` is a string that contains a valid regex, returns the regex constructed with this string.",
39        ]
40    }
41
42    fn notices(&self) -> &'static [&'static str] {
43        &[indoc! {"
44            Compiling a regular expression is an expensive operation and can limit Vector's
45            throughput. Don't use this function unless you are absolutely sure there is no other
46            way!
47        "}]
48    }
49
50    fn parameters(&self) -> &'static [Parameter] {
51        const PARAMETERS: &[Parameter] = &[Parameter::required(
52            "value",
53            kind::BYTES,
54            "The value to convert to a regex.",
55        )];
56        PARAMETERS
57    }
58
59    fn examples(&self) -> &'static [Example] {
60        &[example! {
61            title: "Coerce to a regex",
62            source: r#"to_regex!("^foo$")"#,
63            result: Ok("r'^foo$'"),
64        }]
65    }
66
67    fn compile(
68        &self,
69        _state: &state::TypeState,
70        _ctx: &mut FunctionCompileContext,
71        arguments: ArgumentList,
72    ) -> Compiled {
73        warn!("`to_regex` is an expensive function that could impact throughput.");
74        let value = arguments.required("value");
75        Ok(ToRegexFn { value }.as_expr())
76    }
77}
78
79#[derive(Debug, Clone)]
80struct ToRegexFn {
81    value: Box<dyn Expression>,
82}
83
84impl FunctionExpression for ToRegexFn {
85    fn resolve(&self, ctx: &mut Context) -> Resolved {
86        let value = self.value.resolve(ctx)?;
87        to_regex(&value)
88    }
89
90    fn type_def(&self, _: &state::TypeState) -> TypeDef {
91        TypeDef::regex().fallible()
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    test_function![
100        to_regex => ToRegex;
101
102        regex {
103            args: func_args![value: "^test[A-Za-z_]+$"],
104            want: Ok(regex::Regex::new("^test[A-Za-z_]+$").expect("regex is valid")),
105            tdef: TypeDef::regex().fallible(),
106        }
107
108        invalid_regex {
109            args: func_args![value: "(+)"],
110            want: Err("could not create regex: regex parse error:\n    (+)\n     ^\nerror: repetition operator missing expression"),
111            tdef: TypeDef::regex().fallible(),
112        }
113    ];
114}