vrl/compiler/expression/
abort.rs

1use std::fmt;
2
3use crate::compiler::{
4    Context, Expression, Span, TypeDef,
5    expression::{ExpressionError, Resolved},
6    state::{TypeInfo, TypeState},
7    value::{Kind, VrlValueConvert},
8};
9use crate::diagnostic::{DiagnosticMessage, Label, Note, Urls};
10use crate::parser::ast::Node;
11
12use super::Expr;
13
14#[derive(Debug, Clone, PartialEq)]
15pub struct Abort {
16    span: Span,
17    message: Option<Box<Expr>>,
18}
19
20impl Abort {
21    /// # Errors
22    ///
23    /// * The optional message is fallible.
24    /// * The optional message does not resolve to a string.
25    pub fn new(span: Span, message: Option<Node<Expr>>, state: &TypeState) -> Result<Self, Error> {
26        let message = message
27            .map(|node| {
28                let (expr_span, expr) = node.take();
29                let type_def = expr.type_info(state).result;
30
31                if type_def.is_fallible() {
32                    Err(Error {
33                        variant: ErrorVariant::FallibleExpr,
34                        expr_span,
35                    })
36                } else if !type_def.is_bytes() {
37                    Err(Error {
38                        variant: ErrorVariant::NonString(type_def.into()),
39                        expr_span,
40                    })
41                } else {
42                    Ok(Box::new(expr))
43                }
44            })
45            .transpose()?;
46
47        Ok(Self { span, message })
48    }
49}
50
51impl Expression for Abort {
52    fn resolve(&self, ctx: &mut Context) -> Resolved {
53        let message = self
54            .message
55            .as_ref()
56            .map::<Result<_, ExpressionError>, _>(|expr| {
57                Ok(expr.resolve(ctx)?.try_bytes_utf8_lossy()?.to_string())
58            })
59            .transpose()?;
60
61        Err(ExpressionError::Abort {
62            span: self.span,
63            message,
64        })
65    }
66
67    fn type_info(&self, state: &TypeState) -> TypeInfo {
68        let returns = self.message.as_ref().map_or(Kind::never(), |message| {
69            message.type_info(state).result.returns().to_owned()
70        });
71        TypeInfo::new(state, TypeDef::never().with_returns(returns))
72    }
73}
74
75impl fmt::Display for Abort {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        write!(f, "abort")
78    }
79}
80
81// -----------------------------------------------------------------------------
82
83#[derive(Debug)]
84pub struct Error {
85    variant: ErrorVariant,
86    expr_span: Span,
87}
88
89#[derive(thiserror::Error, Debug)]
90pub(crate) enum ErrorVariant {
91    #[error("unhandled fallible expression")]
92    FallibleExpr,
93    #[error("non-string abort message")]
94    NonString(Kind),
95}
96
97impl fmt::Display for Error {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        write!(f, "{:#}", self.variant)
100    }
101}
102
103impl std::error::Error for Error {
104    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
105        Some(&self.variant)
106    }
107}
108
109impl DiagnosticMessage for Error {
110    fn code(&self) -> usize {
111        use ErrorVariant::{FallibleExpr, NonString};
112
113        match self.variant {
114            FallibleExpr => 631,
115            NonString(_) => 300,
116        }
117    }
118
119    fn labels(&self) -> Vec<Label> {
120        match &self.variant {
121            ErrorVariant::FallibleExpr => vec![
122                Label::primary(
123                    "abort only accepts an infallible expression argument",
124                    self.expr_span,
125                ),
126                Label::context(
127                    "handle errors before using the expression as an abort message",
128                    self.expr_span,
129                ),
130            ],
131            ErrorVariant::NonString(kind) => vec![
132                Label::primary(
133                    "abort only accepts an expression argument resolving to a string",
134                    self.expr_span,
135                ),
136                Label::context(
137                    format!("this expression resolves to {kind}"),
138                    self.expr_span,
139                ),
140            ],
141        }
142    }
143
144    fn notes(&self) -> Vec<Note> {
145        match self.variant {
146            ErrorVariant::FallibleExpr => vec![Note::SeeErrorDocs],
147            ErrorVariant::NonString(_) => vec![
148                Note::CoerceValue,
149                Note::SeeDocs(
150                    "type coercion".to_owned(),
151                    Urls::func_docs("#coerce-functions"),
152                ),
153            ],
154        }
155    }
156}