diff --git a/bindings/nodejs/src/decision.rs b/bindings/nodejs/src/decision.rs index 42a22caf..043d271a 100644 --- a/bindings/nodejs/src/decision.rs +++ b/bindings/nodejs/src/decision.rs @@ -42,7 +42,9 @@ impl ZenDecision { }) .await .map_err(|_| anyhow!("Hook timed out"))? - .map_err(|e| anyhow!(e))?; + .map_err(|e| { + anyhow!(serde_json::to_string(e.as_ref()).unwrap_or_else(|_| e.to_string())) + })?; Ok(serde_json::to_value(&result)?) } diff --git a/bindings/nodejs/src/engine.rs b/bindings/nodejs/src/engine.rs index bade806d..b0167122 100644 --- a/bindings/nodejs/src/engine.rs +++ b/bindings/nodejs/src/engine.rs @@ -74,7 +74,9 @@ impl ZenEngine { }) .await .map_err(|_| anyhow!("Hook timed out"))? - .map_err(|e| anyhow!(e))?; + .map_err(|e| { + anyhow!(serde_json::to_string(e.as_ref()).unwrap_or_else(|_| e.to_string())) + })?; Ok(serde_json::to_value(&result)?) } diff --git a/bindings/python/src/decision.rs b/bindings/python/src/decision.rs index a27bcea2..b38397c0 100644 --- a/bindings/python/src/decision.rs +++ b/bindings/python/src/decision.rs @@ -1,7 +1,7 @@ use crate::engine::PyZenEvaluateOptions; use crate::loader::PyDecisionLoader; use crate::value::PyValue; -use anyhow::Context; +use anyhow::{anyhow, Context}; use pyo3::types::PyDict; use pyo3::{pyclass, pymethods, PyObject, PyResult, Python, ToPyObject}; use pythonize::depythonize; @@ -36,7 +36,9 @@ impl PyZenDecision { trace: options.trace, }, )) - .context("Failed to evaluate graph")?; + .map_err(|e| { + anyhow!(serde_json::to_string(e.as_ref()).unwrap_or_else(|_| e.to_string())) + })?; let value = serde_json::to_value(&result).context("Fail")?; Ok(PyValue(value).to_object(py)) diff --git a/bindings/python/src/engine.rs b/bindings/python/src/engine.rs index 168fa61c..a7c2c40c 100644 --- a/bindings/python/src/engine.rs +++ b/bindings/python/src/engine.rs @@ -1,7 +1,7 @@ use crate::decision::PyZenDecision; use crate::loader::PyDecisionLoader; use crate::value::PyValue; -use anyhow::Context; +use anyhow::{anyhow, Context}; use pyo3::types::PyDict; use pyo3::{pyclass, pymethods, PyObject, PyResult, Python, ToPyObject}; use pythonize::depythonize; @@ -80,7 +80,9 @@ impl PyZenEngine { trace: options.trace, }, )) - .context("Failed to evaluate graph")?; + .map_err(|e| { + anyhow!(serde_json::to_string(e.as_ref()).unwrap_or_else(|_| e.to_string())) + })?; let value = serde_json::to_value(&result).context("Failed to serialize result")?; Ok(PyValue(value).to_object(py)) diff --git a/core/engine/src/error.rs b/core/engine/src/error.rs index 31e4f2b6..3f93b42e 100644 --- a/core/engine/src/error.rs +++ b/core/engine/src/error.rs @@ -1,5 +1,7 @@ use crate::handler::node::NodeError; use crate::loader::LoaderError; +use serde::ser::SerializeMap; +use serde::{Serialize, Serializer}; use thiserror::Error; #[derive(Debug, Error)] @@ -14,6 +16,39 @@ pub enum EvaluationError { DepthLimitExceeded, } +impl Serialize for EvaluationError { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(None)?; + match self { + EvaluationError::DepthLimitExceeded => { + map.serialize_entry("type", "DepthLimitExceeded")?; + } + EvaluationError::NodeError(err) => { + map.serialize_entry("type", "NodeError")?; + map.serialize_entry("nodeId", &err.node_id)?; + map.serialize_entry("source", &err.source.to_string())?; + } + EvaluationError::LoaderError(err) => { + map.serialize_entry("type", "LoaderError")?; + match err.as_ref() { + LoaderError::Internal { key, source } => { + map.serialize_entry("key", key)?; + map.serialize_entry("source", &source.to_string())?; + } + LoaderError::NotFound(key) => { + map.serialize_entry("key", key)?; + } + } + } + } + + map.end() + } +} + impl From for Box { fn from(error: LoaderError) -> Self { Box::new(EvaluationError::LoaderError(error.into()))