use std::fmt;
#[derive(Debug)]
pub enum SdkError {
Engine(hypen_engine::EngineError),
ModuleNotFound(String),
ActionPayload { action: String, message: String },
StateSerde(String),
Route(String),
Component(String),
Other(String),
}
impl fmt::Display for SdkError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SdkError::Engine(e) => write!(f, "Engine error: {e}"),
SdkError::ModuleNotFound(name) => write!(f, "Module not found: {name}"),
SdkError::ActionPayload { action, message } => {
write!(f, "Invalid payload for action '{action}': {message}")
}
SdkError::StateSerde(msg) => write!(f, "State serialization error: {msg}"),
SdkError::Route(msg) => write!(f, "Route error: {msg}"),
SdkError::Component(msg) => write!(f, "Component error: {msg}"),
SdkError::Other(msg) => write!(f, "{msg}"),
}
}
}
impl std::error::Error for SdkError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
SdkError::Engine(e) => Some(e),
_ => None,
}
}
}
impl From<hypen_engine::EngineError> for SdkError {
fn from(e: hypen_engine::EngineError) -> Self {
SdkError::Engine(e)
}
}
impl From<serde_json::Error> for SdkError {
fn from(e: serde_json::Error) -> Self {
SdkError::StateSerde(e.to_string())
}
}
pub type Result<T> = std::result::Result<T, SdkError>;
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
#[test]
fn test_display_all_variants() {
let cases: Vec<(SdkError, &str)> = vec![
(
SdkError::ModuleNotFound("Counter".into()),
"Module not found: Counter",
),
(
SdkError::ActionPayload {
action: "add".into(),
message: "missing field".into(),
},
"Invalid payload for action 'add': missing field",
),
(
SdkError::StateSerde("bad json".into()),
"State serialization error: bad json",
),
(SdkError::Route("no match".into()), "Route error: no match"),
(
SdkError::Component("not found".into()),
"Component error: not found",
),
(SdkError::Other("something".into()), "something"),
];
for (err, expected) in cases {
assert_eq!(err.to_string(), expected);
}
}
#[test]
fn test_display_engine_variant() {
let err = SdkError::Engine(hypen_engine::EngineError::ActionNotFound("missing".into()));
let msg = err.to_string();
assert!(msg.starts_with("Engine error:"));
}
#[test]
fn test_source_engine_returns_some() {
let engine_err = hypen_engine::EngineError::ActionNotFound("x".into());
let err = SdkError::Engine(engine_err);
assert!(err.source().is_some());
}
#[test]
fn test_source_non_engine_returns_none() {
let err = SdkError::Other("hello".into());
assert!(err.source().is_none());
}
#[test]
fn test_from_engine_error() {
let engine_err = hypen_engine::EngineError::ActionNotFound("x".into());
let sdk_err: SdkError = engine_err.into();
assert!(matches!(sdk_err, SdkError::Engine(_)));
}
#[test]
fn test_from_serde_error() {
let serde_err = serde_json::from_str::<i32>("not json").unwrap_err();
let sdk_err: SdkError = serde_err.into();
assert!(matches!(sdk_err, SdkError::StateSerde(_)));
}
}