#[derive(Debug, Clone)]
pub enum CanoError {
NodeExecution(String),
TaskExecution(String),
Preparation(String),
Store(String),
Workflow(String),
Configuration(String),
RetryExhausted(String),
Generic(String),
}
impl CanoError {
pub fn node_execution<S: Into<String>>(msg: S) -> Self {
CanoError::NodeExecution(msg.into())
}
pub fn task_execution<S: Into<String>>(msg: S) -> Self {
CanoError::TaskExecution(msg.into())
}
pub fn preparation<S: Into<String>>(msg: S) -> Self {
CanoError::Preparation(msg.into())
}
pub fn store<S: Into<String>>(msg: S) -> Self {
CanoError::Store(msg.into())
}
pub fn workflow<S: Into<String>>(msg: S) -> Self {
CanoError::Workflow(msg.into())
}
pub fn configuration<S: Into<String>>(msg: S) -> Self {
CanoError::Configuration(msg.into())
}
pub fn retry_exhausted<S: Into<String>>(msg: S) -> Self {
CanoError::RetryExhausted(msg.into())
}
pub fn generic<S: Into<String>>(msg: S) -> Self {
CanoError::Generic(msg.into())
}
pub fn message(&self) -> &str {
match self {
CanoError::NodeExecution(msg) => msg,
CanoError::TaskExecution(msg) => msg,
CanoError::Preparation(msg) => msg,
CanoError::Store(msg) => msg,
CanoError::Workflow(msg) => msg,
CanoError::Configuration(msg) => msg,
CanoError::RetryExhausted(msg) => msg,
CanoError::Generic(msg) => msg,
}
}
pub fn category(&self) -> &'static str {
match self {
CanoError::NodeExecution(_) => "node_execution",
CanoError::TaskExecution(_) => "task_execution",
CanoError::Preparation(_) => "preparation",
CanoError::Store(_) => "store",
CanoError::Workflow(_) => "workflow",
CanoError::Configuration(_) => "configuration",
CanoError::RetryExhausted(_) => "retry_exhausted",
CanoError::Generic(_) => "generic",
}
}
}
impl std::fmt::Display for CanoError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CanoError::NodeExecution(msg) => write!(f, "Node execution error: {msg}"),
CanoError::TaskExecution(msg) => write!(f, "Task execution error: {msg}"),
CanoError::Preparation(msg) => write!(f, "Preparation error: {msg}"),
CanoError::Store(msg) => write!(f, "Store error: {msg}"),
CanoError::Workflow(msg) => write!(f, "Workflow error: {msg}"),
CanoError::Configuration(msg) => write!(f, "Configuration error: {msg}"),
CanoError::RetryExhausted(msg) => write!(f, "Retry exhausted: {msg}"),
CanoError::Generic(msg) => write!(f, "Error: {msg}"),
}
}
}
impl std::error::Error for CanoError {}
impl PartialEq for CanoError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(CanoError::NodeExecution(a), CanoError::NodeExecution(b)) => a == b,
(CanoError::TaskExecution(a), CanoError::TaskExecution(b)) => a == b,
(CanoError::Preparation(a), CanoError::Preparation(b)) => a == b,
(CanoError::Store(a), CanoError::Store(b)) => a == b,
(CanoError::Workflow(a), CanoError::Workflow(b)) => a == b,
(CanoError::Configuration(a), CanoError::Configuration(b)) => a == b,
(CanoError::RetryExhausted(a), CanoError::RetryExhausted(b)) => a == b,
(CanoError::Generic(a), CanoError::Generic(b)) => a == b,
_ => false,
}
}
}
impl Eq for CanoError {}
impl From<Box<dyn std::error::Error + Send + Sync>> for CanoError {
fn from(err: Box<dyn std::error::Error + Send + Sync>) -> Self {
CanoError::Generic(err.to_string())
}
}
impl From<&str> for CanoError {
fn from(err: &str) -> Self {
CanoError::Generic(err.to_string())
}
}
impl From<String> for CanoError {
fn from(err: String) -> Self {
CanoError::Generic(err)
}
}
impl From<std::io::Error> for CanoError {
fn from(err: std::io::Error) -> Self {
CanoError::Generic(format!("IO error: {err}"))
}
}
impl From<crate::store::error::StoreError> for CanoError {
fn from(err: crate::store::error::StoreError) -> Self {
CanoError::store(err.to_string())
}
}
pub type CanoResult<TState> = Result<TState, CanoError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_creation() {
let error = CanoError::node_execution("Test error");
assert_eq!(error.message(), "Test error");
assert_eq!(error.category(), "node_execution");
}
#[test]
fn test_error_display() {
let error = CanoError::NodeExecution("Test error".to_string());
assert_eq!(format!("{error}"), "Node execution error: Test error");
}
#[test]
fn test_error_conversions() {
let error1: CanoError = "Test error".into();
let error2: CanoError = "Test error".to_string().into();
match (&error1, &error2) {
(CanoError::Generic(msg1), CanoError::Generic(msg2)) => {
assert_eq!(msg1, msg2);
}
_ => panic!("Expected Generic errors"),
}
}
#[test]
fn test_error_categories() {
assert_eq!(
CanoError::NodeExecution("".to_string()).category(),
"node_execution"
);
assert_eq!(CanoError::store("".to_string()).category(), "store");
assert_eq!(CanoError::Workflow("".to_string()).category(), "workflow");
}
#[test]
fn test_store_error_conversion() {
use crate::store::error::StoreError;
let store_error = StoreError::key_not_found("test_key");
let cano_error: CanoError = store_error.into();
match cano_error {
CanoError::Store(msg) => {
assert!(msg.contains("test_key"));
assert!(msg.contains("not found"));
}
_ => panic!("Expected store error variant"),
}
let store_error = StoreError::type_mismatch("type error");
let cano_error: CanoError = store_error.into();
assert_eq!(cano_error.category(), "store");
let store_error = StoreError::lock_error("lock failed");
let cano_error: CanoError = store_error.into();
assert_eq!(cano_error.category(), "store");
assert!(cano_error.message().contains("lock failed"));
}
#[test]
fn test_io_error_maps_to_generic() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
let cano_err: CanoError = io_err.into();
assert_eq!(cano_err.category(), "generic");
assert!(cano_err.message().contains("IO error"));
assert!(cano_err.message().contains("file missing"));
}
#[test]
fn test_partial_eq_same_variant_same_message() {
let a = CanoError::NodeExecution("oops".to_string());
let b = CanoError::NodeExecution("oops".to_string());
assert_eq!(a, b);
}
#[test]
fn test_partial_eq_same_variant_different_message() {
let a = CanoError::NodeExecution("a".to_string());
let b = CanoError::NodeExecution("b".to_string());
assert_ne!(a, b);
}
#[test]
fn test_partial_eq_different_variants() {
let a = CanoError::NodeExecution("msg".to_string());
let b = CanoError::TaskExecution("msg".to_string());
assert_ne!(a, b);
}
}