Skip to main content

zen_engine/
error.rs

1use crate::engine::EvaluationTraceKind;
2use crate::loader::LoaderError;
3use crate::DecisionGraphValidationError;
4use serde::ser::SerializeMap;
5use serde::{Serialize, Serializer};
6use serde_json::Value;
7use std::fmt;
8use std::sync::Arc;
9use thiserror::Error;
10use zen_types::variable::Variable;
11
12#[derive(Debug, Error)]
13#[error("expected {expected} content, got {got}")]
14pub struct ContentKindError {
15    pub expected: &'static str,
16    pub got: &'static str,
17}
18
19#[derive(Debug, Clone, Serialize)]
20#[serde(rename_all = "camelCase")]
21pub struct CompileFailure {
22    pub key: Arc<str>,
23    pub kind: &'static str,
24    #[serde(skip_serializing_if = "Vec::is_empty")]
25    pub diagnostics: Vec<crate::policy::Diagnostic>,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub error: Option<String>,
28}
29
30impl fmt::Display for CompileFailure {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        write!(f, "{} [{}]: ", self.key, self.kind)?;
33        match &self.error {
34            Some(error) => write!(f, "{error}"),
35            None => {
36                let messages: Vec<&str> = self
37                    .diagnostics
38                    .iter()
39                    .map(|d| d.message.as_str())
40                    .collect();
41                write!(f, "{}", messages.join("; "))
42            }
43        }
44    }
45}
46
47#[derive(Debug, Error)]
48pub enum EvaluationError {
49    #[error("Loader error")]
50    LoaderError(LoaderError),
51
52    #[error("{source}")]
53    NodeError {
54        node_id: Arc<str>,
55        trace: Option<Variable>,
56        source: Box<dyn std::error::Error>,
57    },
58
59    #[error("Depth limit exceeded")]
60    DepthLimitExceeded,
61
62    #[error("Invalid graph")]
63    InvalidGraph(DecisionGraphValidationError),
64
65    #[error("Validation failed")]
66    Validation(Value),
67
68    #[error("policy evaluation error: {0}")]
69    Policy(crate::policy::EvaluationError),
70
71    #[error("expected {expected} content for key '{key}', got {got}")]
72    ContentKindMismatch {
73        expected: &'static str,
74        got: &'static str,
75        key: Arc<str>,
76    },
77}
78
79impl EvaluationError {
80    pub fn serialize_with_mode<S>(
81        &self,
82        serializer: S,
83        mode: EvaluationTraceKind,
84    ) -> Result<S::Ok, S::Error>
85    where
86        S: Serializer,
87    {
88        let mut map = serializer.serialize_map(None)?;
89
90        match self {
91            EvaluationError::DepthLimitExceeded => {
92                map.serialize_entry("type", "DepthLimitExceeded")?;
93            }
94            EvaluationError::NodeError {
95                node_id,
96                trace,
97                source,
98            } => {
99                map.serialize_entry("type", "NodeError")?;
100                map.serialize_entry("source", &source.to_string())?;
101                map.serialize_entry("nodeId", &node_id)?;
102
103                if let Some(trace) = &trace {
104                    map.serialize_entry("trace", &mode.serialize_trace(trace))?;
105                }
106            }
107            EvaluationError::LoaderError(err) => {
108                map.serialize_entry("type", "LoaderError")?;
109                match err {
110                    LoaderError::Internal { key, source } => {
111                        map.serialize_entry("key", key)?;
112                        map.serialize_entry("source", &source.to_string())?;
113                    }
114                    LoaderError::NotFound(key) => {
115                        map.serialize_entry("key", key)?;
116                    }
117                }
118            }
119            EvaluationError::InvalidGraph(err) => {
120                map.serialize_entry("type", "InvalidGraph")?;
121                map.serialize_entry("source", err)?;
122            }
123            EvaluationError::Validation(err) => {
124                map.serialize_entry("type", "Validation")?;
125                map.serialize_entry("source", err)?;
126            }
127            EvaluationError::Policy(err) => {
128                map.serialize_entry("type", "PolicyError")?;
129                err.serialize_into_map(&mut map)?;
130            }
131            EvaluationError::ContentKindMismatch { expected, got, key } => {
132                map.serialize_entry("type", "ContentKindMismatch")?;
133                map.serialize_entry("expected", expected)?;
134                map.serialize_entry("got", got)?;
135                map.serialize_entry("key", key)?;
136            }
137        }
138
139        map.end()
140    }
141}
142
143impl Serialize for EvaluationError {
144    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
145    where
146        S: Serializer,
147    {
148        self.serialize_with_mode(serializer, Default::default())
149    }
150}
151
152impl From<LoaderError> for Box<EvaluationError> {
153    fn from(error: LoaderError) -> Self {
154        Box::new(EvaluationError::LoaderError(error.into()))
155    }
156}
157
158impl From<DecisionGraphValidationError> for Box<EvaluationError> {
159    fn from(error: DecisionGraphValidationError) -> Self {
160        Box::new(EvaluationError::InvalidGraph(error.into()))
161    }
162}
163
164impl From<crate::policy::EvaluationError> for Box<EvaluationError> {
165    fn from(error: crate::policy::EvaluationError) -> Self {
166        Box::new(EvaluationError::Policy(error))
167    }
168}