vrl/compiler/expression/
predicate.rs

1use std::fmt;
2
3use crate::diagnostic::{DiagnosticMessage, Label, Note, Urls};
4
5use crate::compiler::expression::Block;
6use crate::compiler::{
7    Context, Expression, Span,
8    expression::{Expr, Resolved},
9    parser::Node,
10    state::{TypeInfo, TypeState},
11    value::Kind,
12};
13
14pub(crate) type Result = std::result::Result<Predicate, Error>;
15
16#[derive(Clone, PartialEq)]
17pub struct Predicate {
18    inner: Block,
19}
20
21impl Predicate {
22    pub(crate) fn new(
23        node: Node<Vec<Expr>>,
24        state: &TypeState,
25        fallible_predicate: Option<&dyn DiagnosticMessage>,
26    ) -> Result {
27        let (span, exprs) = node.take();
28
29        if let Some(error) = fallible_predicate {
30            return Err(Error::Fallible {
31                code: error.code(),
32                labels: error.labels(),
33                notes: error.notes(),
34            });
35        }
36
37        let block = Block::new_inline(exprs);
38        let type_def = block.type_info(state).result;
39        if !type_def.is_boolean() {
40            return Err(Error::NonBoolean {
41                kind: type_def.into(),
42                span,
43            });
44        }
45
46        Ok(Self { inner: block })
47    }
48
49    #[must_use]
50    pub fn new_unchecked(inner: Vec<Expr>) -> Self {
51        Self {
52            inner: Block::new_inline(inner),
53        }
54    }
55}
56
57impl Expression for Predicate {
58    fn resolve(&self, ctx: &mut Context) -> Resolved {
59        self.inner.resolve(ctx)
60    }
61
62    fn type_info(&self, state: &TypeState) -> TypeInfo {
63        self.inner.type_info(state)
64    }
65}
66
67impl fmt::Display for Predicate {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        if self.inner.exprs().len() > 1 {
70            f.write_str("(")?;
71        }
72
73        let mut iter = self.inner.exprs().iter().peekable();
74        while let Some(expr) = iter.next() {
75            expr.fmt(f)?;
76
77            if iter.peek().is_some() {
78                f.write_str("; ")?;
79            }
80        }
81
82        if self.inner.exprs().len() > 1 {
83            f.write_str("(")?;
84        }
85
86        Ok(())
87    }
88}
89
90impl fmt::Debug for Predicate {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        f.write_str("Predicate(")?;
93
94        let mut iter = self.inner.exprs().iter().peekable();
95        while let Some(expr) = iter.next() {
96            expr.fmt(f)?;
97
98            if iter.peek().is_some() {
99                f.write_str("; ")?;
100            }
101        }
102
103        f.write_str(")")
104    }
105}
106
107// -----------------------------------------------------------------------------
108
109#[derive(thiserror::Error, Debug)]
110pub(crate) enum Error {
111    #[error("non-boolean predicate")]
112    NonBoolean { kind: Kind, span: Span },
113
114    #[error("fallible predicate")]
115    Fallible {
116        code: usize,
117        labels: Vec<Label>,
118        notes: Vec<Note>,
119    },
120}
121
122impl DiagnosticMessage for Error {
123    fn code(&self) -> usize {
124        use Error::{Fallible, NonBoolean};
125
126        match self {
127            NonBoolean { .. } => 102,
128            Fallible { code, .. } => *code,
129        }
130    }
131
132    fn labels(&self) -> Vec<Label> {
133        use Error::{Fallible, NonBoolean};
134
135        match self {
136            NonBoolean { kind, span } => vec![
137                Label::primary("this predicate must resolve to a boolean", span),
138                Label::context(format!("instead it resolves to {kind}"), span),
139            ],
140            Fallible { labels, .. } => labels.clone(),
141        }
142    }
143
144    fn notes(&self) -> Vec<Note> {
145        use Error::{Fallible, NonBoolean};
146
147        match self {
148            NonBoolean { .. } => vec![
149                Note::CoerceValue,
150                Note::SeeDocs(
151                    "if expressions".to_owned(),
152                    Urls::expression_docs_url("#if"),
153                ),
154            ],
155            Fallible { notes, .. } => notes.clone(),
156        }
157    }
158}