hcl/eval/
error.rs

1use super::*;
2use std::fmt;
3
4/// The result type used by this module.
5pub type EvalResult<T, E = Error> = std::result::Result<T, E>;
6
7pub(super) trait EvalResultExt {
8    fn add_errors(self, rhs: Self) -> Self;
9}
10
11impl EvalResultExt for EvalResult<(), Errors> {
12    fn add_errors(self, rhs: Self) -> Self {
13        match self {
14            Err(mut lhs) => {
15                lhs.extend_from_result(rhs);
16                Err(lhs)
17            }
18            _ => rhs,
19        }
20    }
21}
22
23/// A type holding multiple errors that occurred during in-place expression evaluation via
24/// [`Evaluate::evaluate_in_place`].
25///
26/// It is guaranteed that `Errors` instances hold at least one error.
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct Errors {
29    inner: Vec<Error>,
30}
31
32impl Errors {
33    fn extend_from_result(&mut self, res: EvalResult<(), Errors>) {
34        if let Err(errors) = res {
35            self.inner.extend(errors);
36        }
37    }
38
39    /// Returns the number of errors.
40    #[inline]
41    #[allow(clippy::len_without_is_empty)]
42    pub fn len(&self) -> usize {
43        self.inner.len()
44    }
45
46    /// Returns an iterator over all errors.
47    #[inline]
48    pub fn iter(&self) -> std::slice::Iter<Error> {
49        self.inner.iter()
50    }
51}
52
53impl fmt::Display for Errors {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        if self.len() == 1 {
56            self.inner[0].fmt(f)
57        } else {
58            writeln!(f, "{} errors occurred:", self.len())?;
59
60            for error in self {
61                writeln!(f, "- {error}")?;
62            }
63
64            Ok(())
65        }
66    }
67}
68
69impl From<Error> for Errors {
70    #[inline]
71    fn from(error: Error) -> Self {
72        Errors { inner: vec![error] }
73    }
74}
75
76impl std::error::Error for Errors {}
77
78impl IntoIterator for Errors {
79    type Item = Error;
80    type IntoIter = std::vec::IntoIter<Error>;
81
82    fn into_iter(self) -> Self::IntoIter {
83        self.inner.into_iter()
84    }
85}
86
87impl<'a> IntoIterator for &'a Errors {
88    type Item = &'a Error;
89    type IntoIter = std::slice::Iter<'a, Error>;
90
91    fn into_iter(self) -> Self::IntoIter {
92        self.iter()
93    }
94}
95
96/// The error type returned by all fallible operations within this module.
97#[derive(Debug, Clone, PartialEq, Eq)]
98pub struct Error {
99    inner: Box<ErrorInner>,
100}
101
102impl Error {
103    pub(super) fn new<T>(kind: T) -> Error
104    where
105        T: Into<ErrorKind>,
106    {
107        Error::new_with_expr(kind, None)
108    }
109
110    pub(super) fn new_with_expr<T>(kind: T, expr: Option<Expression>) -> Error
111    where
112        T: Into<ErrorKind>,
113    {
114        Error {
115            inner: Box::new(ErrorInner::new(kind.into(), expr)),
116        }
117    }
118
119    pub(super) fn unexpected<T>(value: T, expected: &'static str) -> Error
120    where
121        T: Into<Value>,
122    {
123        Error::new(ErrorKind::Unexpected(value.into(), expected))
124    }
125
126    /// Return a reference to the `ErrorKind` for further error matching.
127    pub fn kind(&self) -> &ErrorKind {
128        &self.inner.kind
129    }
130
131    /// Return a reference to the `Expression` that caused the error, if there is one.
132    pub fn expr(&self) -> Option<&Expression> {
133        self.inner.expr.as_ref()
134    }
135
136    /// Consume the `Error` and return the `ErrorKind`.
137    pub fn into_kind(self) -> ErrorKind {
138        self.inner.kind
139    }
140}
141
142impl fmt::Display for Error {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        fmt::Display::fmt(&self.inner, f)
145    }
146}
147
148impl From<ErrorKind> for Error {
149    fn from(kind: ErrorKind) -> Self {
150        Error::new(kind)
151    }
152}
153
154impl From<crate::Error> for Error {
155    fn from(err: crate::Error) -> Self {
156        Error::new(ErrorKind::Message(err.to_string()))
157    }
158}
159
160impl std::error::Error for Error {}
161
162// The inner type that holds the actual error data.
163//
164// This is a separate type because it gets boxed to keep the size of the `Error` struct small.
165#[derive(Debug, Clone, PartialEq, Eq)]
166struct ErrorInner {
167    kind: ErrorKind,
168    expr: Option<Expression>,
169}
170
171impl ErrorInner {
172    fn new(kind: ErrorKind, expr: Option<Expression>) -> ErrorInner {
173        ErrorInner { kind, expr }
174    }
175}
176
177impl fmt::Display for ErrorInner {
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        write!(f, "{}", self.kind)?;
180
181        if let Some(expr) = &self.expr {
182            write!(f, " in expression `{expr}`")?;
183        }
184
185        Ok(())
186    }
187}
188
189/// An enum representing all kinds of errors that can happen during the evaluation of HCL
190/// expressions and templates.
191#[derive(Debug, Clone, PartialEq, Eq)]
192#[non_exhaustive]
193pub enum ErrorKind {
194    /// A generic error message.
195    Message(String),
196    /// An expression contained an undefined variable.
197    UndefinedVar(Identifier),
198    /// An expression contained a call to an undefined function.
199    UndefinedFunc(FuncName),
200    /// A different type of value was expected.
201    Unexpected(Value, &'static str),
202    /// An expression tried to access a non-existing array index.
203    Index(usize),
204    /// An unary operator was applied to a value that does not support it.
205    UnaryOp(UnaryOperator, Value),
206    /// A binary operator was applied to values that do not support it.
207    BinaryOp(Value, BinaryOperator, Value),
208    /// An expression tried to access an object key which does not exist.
209    NoSuchKey(String),
210    /// A `for` expression attempted to set the same object key twice.
211    KeyExists(String),
212    /// A function call in an expression returned an error.
213    FuncCall(FuncName, String),
214}
215
216impl From<Error> for ErrorKind {
217    fn from(err: Error) -> Self {
218        err.into_kind()
219    }
220}
221
222impl From<&str> for ErrorKind {
223    fn from(msg: &str) -> Self {
224        ErrorKind::Message(msg.to_owned())
225    }
226}
227
228impl From<String> for ErrorKind {
229    fn from(msg: String) -> Self {
230        ErrorKind::Message(msg)
231    }
232}
233
234impl fmt::Display for ErrorKind {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        match self {
237            ErrorKind::Message(msg) => f.write_str(msg),
238            ErrorKind::UndefinedVar(ident) => {
239                write!(f, "undefined variable `{ident}`")
240            }
241            ErrorKind::UndefinedFunc(func_name) => {
242                write!(f, "undefined function `{func_name}`")
243            }
244            ErrorKind::Unexpected(value, expected) => {
245                write!(f, "unexpected value `{value}`, expected {expected}")
246            }
247            ErrorKind::Index(index) => write!(f, "index out of bounds: {index}"),
248            ErrorKind::NoSuchKey(key) => write!(f, "no such key: `{key}`"),
249            ErrorKind::KeyExists(key) => write!(f, "key `{key}` already exists"),
250            ErrorKind::UnaryOp(operator, value) => write!(
251                f,
252                "unary operator `{operator}` is not applicable to `{value}`",
253            ),
254            ErrorKind::BinaryOp(lhs, operator, rhs) => write!(
255                f,
256                "binary operator `{operator}` is not applicable to `{lhs}` and `{rhs}`",
257            ),
258            ErrorKind::FuncCall(name, msg) => {
259                write!(f, "error calling function `{name}`: {msg}")
260            }
261        }
262    }
263}