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}