vrl/compiler/expression/
not.rs

1use std::fmt;
2
3use crate::compiler::state::{TypeInfo, TypeState};
4use crate::compiler::{
5    Context, Expression, Span, TypeDef,
6    expression::{Expr, Resolved},
7    parser::Node,
8    value::{Kind, VrlValueConvert},
9};
10use crate::diagnostic::{DiagnosticMessage, Label, Note, Urls};
11
12pub(crate) type Result = std::result::Result<Not, Error>;
13
14#[derive(Debug, Clone, PartialEq)]
15pub struct Not {
16    inner: Box<Expr>,
17}
18
19impl Not {
20    /// Creates a new `Not` expression.
21    ///
22    /// # Errors
23    /// Returns an `Error` if the provided expression's type is not boolean.
24    ///
25    /// # Arguments
26    /// * `node` - The node representing the expression.
27    /// * `not_span` - The span of the `not` operator.
28    /// * `state` - The current type state.
29    ///
30    /// # Returns
31    /// A `Result` containing the new `Not` expression or an error.
32    pub fn new(node: Node<Expr>, not_span: Span, state: &TypeState) -> Result {
33        let (expr_span, expr) = node.take();
34        let type_def = expr.type_info(state).result;
35
36        if !type_def.is_boolean() {
37            return Err(Error {
38                variant: ErrorVariant::NonBoolean(type_def.into()),
39                not_span,
40                expr_span,
41            });
42        }
43
44        Ok(Self {
45            inner: Box::new(expr),
46        })
47    }
48}
49
50impl Expression for Not {
51    fn resolve(&self, ctx: &mut Context) -> Resolved {
52        Ok((!self.inner.resolve(ctx)?.try_boolean()?).into())
53    }
54
55    fn type_info(&self, state: &TypeState) -> TypeInfo {
56        let mut state = state.clone();
57        let result = self.inner.apply_type_info(&mut state);
58        TypeInfo::new(
59            state,
60            TypeDef::boolean()
61                .maybe_fallible(result.is_fallible())
62                .with_returns(result.returns().clone()),
63        )
64    }
65}
66
67impl fmt::Display for Not {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        write!(f, "!{}", self.inner)
70    }
71}
72
73// -----------------------------------------------------------------------------
74
75#[derive(Debug)]
76pub struct Error {
77    pub(crate) variant: ErrorVariant,
78
79    not_span: Span,
80    expr_span: Span,
81}
82
83#[derive(thiserror::Error, Debug)]
84pub(crate) enum ErrorVariant {
85    #[error("non-boolean negation")]
86    NonBoolean(Kind),
87}
88
89impl fmt::Display for Error {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        write!(f, "{:#}", self.variant)
92    }
93}
94
95impl std::error::Error for Error {
96    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
97        Some(&self.variant)
98    }
99}
100
101impl DiagnosticMessage for Error {
102    fn code(&self) -> usize {
103        use ErrorVariant::NonBoolean;
104
105        match &self.variant {
106            NonBoolean(..) => 660,
107        }
108    }
109
110    fn labels(&self) -> Vec<Label> {
111        use ErrorVariant::NonBoolean;
112
113        match &self.variant {
114            NonBoolean(kind) => vec![
115                Label::primary("negation only works on boolean values", self.not_span),
116                Label::context(
117                    format!("this expression resolves to {kind}"),
118                    self.expr_span,
119                ),
120            ],
121        }
122    }
123
124    fn notes(&self) -> Vec<Note> {
125        use ErrorVariant::NonBoolean;
126
127        match &self.variant {
128            NonBoolean(..) => {
129                vec![
130                    Note::CoerceValue,
131                    Note::SeeDocs(
132                        "type coercion".to_owned(),
133                        Urls::func_docs("#coerce-functions"),
134                    ),
135                ]
136            }
137        }
138    }
139}