vrl/stdlib/
assert_eq.rs

1use crate::compiler::prelude::*;
2
3fn assert_eq(left: &Value, right: &Value, message: Option<Value>) -> Resolved {
4    if left == right {
5        Ok(true.into())
6    } else if let Some(message) = message {
7        let message = message.try_bytes_utf8_lossy()?.into_owned();
8        Err(ExpressionError::Error {
9            message: message.clone(),
10            labels: vec![],
11            notes: vec![Note::UserErrorMessage(message)],
12        })
13    } else {
14        Err(ExpressionError::from(format!(
15            "assertion failed: {left} == {right}"
16        )))
17    }
18}
19
20#[derive(Clone, Copy, Debug)]
21pub struct AssertEq;
22
23impl Function for AssertEq {
24    fn identifier(&self) -> &'static str {
25        "assert_eq"
26    }
27
28    fn usage(&self) -> &'static str {
29        "Asserts that two expressions, `left` and `right`, have the same value. The program is aborted with `message` if they do not have the same value."
30    }
31
32    fn category(&self) -> &'static str {
33        Category::Debug.as_ref()
34    }
35
36    fn return_kind(&self) -> u16 {
37        kind::BOOLEAN
38    }
39
40    fn notices(&self) -> &'static [&'static str] {
41        &[indoc! {"
42            The `assert_eq` function should be used in a standalone fashion and only when you want
43            to abort the program. You should avoid it in logical expressions and other situations in
44            which you want the program to continue if the condition evaluates to `false`.
45        "}]
46    }
47
48    fn pure(&self) -> bool {
49        false
50    }
51
52    fn parameters(&self) -> &'static [Parameter] {
53        const PARAMETERS: &[Parameter] = &[
54            Parameter::required(
55                "left",
56                kind::ANY,
57                "The value to check for equality against `right`.",
58            ),
59            Parameter::required(
60                "right",
61                kind::ANY,
62                "The value to check for equality against `left`.",
63            ),
64            Parameter::optional(
65                "message",
66                kind::BYTES,
67                "An optional custom error message. If the equality assertion fails, `message` is
68appended to the default message prefix. See the [examples](#assert_eq-examples)
69below for a fully formed log message sample.",
70            ),
71        ];
72        PARAMETERS
73    }
74
75    fn examples(&self) -> &'static [Example] {
76        &[
77            example! {
78                title: "Successful assertion",
79                source: "assert_eq!(1, 1)",
80                result: Ok("true"),
81            },
82            example! {
83                title: "Unsuccessful assertion",
84                source: "assert_eq!(127, [1, 2, 3])",
85                result: Err(
86                    r#"function call error for "assert_eq" at (0:26): assertion failed: 127 == [1, 2, 3]"#,
87                ),
88            },
89            example! {
90                title: "Unsuccessful assertion with custom log message",
91                source: r#"assert_eq!(1, 0, message: "Unequal integers")"#,
92                result: Err(r#"function call error for "assert_eq" at (0:45): Unequal integers"#),
93            },
94        ]
95    }
96
97    fn compile(
98        &self,
99        _state: &state::TypeState,
100        _ctx: &mut FunctionCompileContext,
101        arguments: ArgumentList,
102    ) -> Compiled {
103        let left = arguments.required("left");
104        let right = arguments.required("right");
105        let message = arguments.optional("message");
106
107        Ok(AssertEqFn {
108            left,
109            right,
110            message,
111        }
112        .as_expr())
113    }
114}
115
116#[derive(Debug, Clone)]
117struct AssertEqFn {
118    left: Box<dyn Expression>,
119    right: Box<dyn Expression>,
120    message: Option<Box<dyn Expression>>,
121}
122
123impl FunctionExpression for AssertEqFn {
124    fn resolve(&self, ctx: &mut Context) -> Resolved {
125        let left = self.left.resolve(ctx)?;
126        let right = self.right.resolve(ctx)?;
127        let message = self.message.as_ref().map(|m| m.resolve(ctx)).transpose()?;
128
129        assert_eq(&left, &right, message)
130    }
131
132    fn type_def(&self, _: &state::TypeState) -> TypeDef {
133        TypeDef::boolean().fallible()
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    test_function![
142        assert_eq => AssertEq;
143
144        pass {
145            args: func_args![left: "foo", right: "foo"],
146            want: Ok(true),
147            tdef: TypeDef::boolean().fallible(),
148        }
149
150        fail {
151            args: func_args![left: "foo", right: "bar"],
152            want: Err(r#"assertion failed: "foo" == "bar""#),
153            tdef: TypeDef::boolean().fallible(),
154        }
155
156        message {
157            args: func_args![left: "foo", right: "bar", message: "failure!"],
158            want: Err("failure!"),
159            tdef: TypeDef::boolean().fallible(),
160        }
161    ];
162}