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}