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}