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)]
48#[non_exhaustive]
49pub enum EvaluationError {
50    #[error("Loader error")]
51    LoaderError(LoaderError),
52
53    #[error("{source}")]
54    NodeError {
55        node_id: Arc<str>,
56        trace: Option<Variable>,
57        source: Box<dyn std::error::Error>,
58    },
59
60    #[error("Depth limit exceeded")]
61    DepthLimitExceeded,
62
63    #[error("Invalid graph")]
64    InvalidGraph(DecisionGraphValidationError),
65
66    #[error("Validation failed")]
67    Validation(Value),
68
69    #[error("policy evaluation error: {0}")]
70    Policy(crate::policy::EvaluationError),
71
72    #[error("expected {expected} content for key '{key}', got {got}")]
73    ContentKindMismatch {
74        expected: &'static str,
75        got: &'static str,
76        key: Arc<str>,
77    },
78}
79
80impl EvaluationError {
81    pub fn serialize_with_mode<S>(
82        &self,
83        serializer: S,
84        mode: EvaluationTraceKind,
85    ) -> Result<S::Ok, S::Error>
86    where
87        S: Serializer,
88    {
89        let mut map = serializer.serialize_map(None)?;
90
91        match self {
92            EvaluationError::DepthLimitExceeded => {
93                map.serialize_entry("type", "DepthLimitExceeded")?;
94            }
95            EvaluationError::NodeError {
96                node_id,
97                trace,
98                source,
99            } => {
100                map.serialize_entry("type", "NodeError")?;
101                map.serialize_entry("source", &source.to_string())?;
102                map.serialize_entry("nodeId", &node_id)?;
103
104                if let Some(trace) = &trace {
105                    map.serialize_entry("trace", &mode.serialize_trace(trace))?;
106                }
107            }
108            EvaluationError::LoaderError(err) => {
109                map.serialize_entry("type", "LoaderError")?;
110                match err {
111                    LoaderError::Internal { key, source } => {
112                        map.serialize_entry("key", key)?;
113                        map.serialize_entry("source", &source.to_string())?;
114                    }
115                    LoaderError::NotFound(key) => {
116                        map.serialize_entry("key", key)?;
117                    }
118                }
119            }
120            EvaluationError::InvalidGraph(err) => {
121                map.serialize_entry("type", "InvalidGraph")?;
122                map.serialize_entry("source", err)?;
123            }
124            EvaluationError::Validation(err) => {
125                map.serialize_entry("type", "Validation")?;
126                map.serialize_entry("source", err)?;
127            }
128            EvaluationError::Policy(err) => {
129                map.serialize_entry("type", "PolicyError")?;
130                err.serialize_into_map(&mut map)?;
131            }
132            EvaluationError::ContentKindMismatch { expected, got, key } => {
133                map.serialize_entry("type", "ContentKindMismatch")?;
134                map.serialize_entry("expected", expected)?;
135                map.serialize_entry("got", got)?;
136                map.serialize_entry("key", key)?;
137            }
138        }
139
140        map.end()
141    }
142}
143
144impl Serialize for EvaluationError {
145    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
146    where
147        S: Serializer,
148    {
149        self.serialize_with_mode(serializer, Default::default())
150    }
151}
152
153impl From<LoaderError> for Box<EvaluationError> {
154    fn from(error: LoaderError) -> Self {
155        Box::new(EvaluationError::LoaderError(error.into()))
156    }
157}
158
159impl From<DecisionGraphValidationError> for Box<EvaluationError> {
160    fn from(error: DecisionGraphValidationError) -> Self {
161        Box::new(EvaluationError::InvalidGraph(error.into()))
162    }
163}
164
165impl From<crate::policy::EvaluationError> for Box<EvaluationError> {
166    fn from(error: crate::policy::EvaluationError) -> Self {
167        Box::new(EvaluationError::Policy(error))
168    }
169}