use thiserror::Error;
#[derive(Error, Debug)]
pub enum EngineError {
#[error("thinker failed: {0}")]
ThinkFailed(String),
#[error("output processor failed: {0}")]
ProcessFailed(String),
#[error("context builder failed: {0}")]
ContextFailed(String),
#[error("engine configuration error: {0}")]
Config(String),
#[error("engine cancelled")]
Cancelled,
}
impl EngineError {
pub fn is_retryable(&self) -> bool {
match self {
EngineError::Cancelled => true,
EngineError::ThinkFailed(_) => true,
EngineError::ContextFailed(_) => false,
EngineError::ProcessFailed(_) => false,
EngineError::Config(_) => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cancelled_is_retryable() {
assert!(EngineError::Cancelled.is_retryable());
}
#[test]
fn test_think_failed_is_retryable() {
assert!(EngineError::ThinkFailed("rate limited".into()).is_retryable());
}
#[test]
fn test_process_failed_not_retryable() {
assert!(!EngineError::ProcessFailed("invalid input".into()).is_retryable());
}
#[test]
fn test_context_failed_not_retryable() {
assert!(!EngineError::ContextFailed("broken".into()).is_retryable());
}
#[test]
fn test_config_not_retryable() {
assert!(!EngineError::Config("thinker is required".into()).is_retryable());
}
#[test]
fn test_debug_display() {
let e = EngineError::ThinkFailed("timeout".into());
assert_eq!(format!("{}", e), "thinker failed: timeout");
}
}