use thiserror::Error;
#[derive(Debug, Clone, Error)]
pub enum AiError {
#[error("AI provider network error: {0}")]
Network(String),
#[error("AI provider authentication failed: {0}")]
Auth(String),
#[error("AI provider request timed out: {0}")]
Timeout(String),
#[error("AI provider rate limited: {0}")]
RateLimited(String),
#[error("AI provider budget exceeded: {0}")]
BudgetExceeded(String),
#[error("AI provider error: {0}")]
ProviderError(String),
#[error("AI provider unavailable: {0}")]
Unavailable(String),
#[error("invalid AI provider response: {0}")]
InvalidResponse(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn variants_format_distinct_messages() {
let errs = [
AiError::Network("dns".into()),
AiError::Auth("401".into()),
AiError::Timeout("30s".into()),
AiError::RateLimited("429".into()),
AiError::BudgetExceeded("cap".into()),
AiError::ProviderError("500".into()),
AiError::Unavailable("down".into()),
AiError::InvalidResponse("bad id".into()),
];
let msgs: Vec<String> = errs.iter().map(|e| format!("{e}")).collect();
for (i, m) in msgs.iter().enumerate() {
for (j, n) in msgs.iter().enumerate() {
if i != j {
assert_ne!(m, n, "variants {i} and {j} produced identical messages");
}
}
}
}
#[test]
fn debug_output_is_serializable_to_string() {
let e = AiError::ProviderError("boom".into());
let dbg = format!("{e:?}");
assert!(dbg.contains("ProviderError"));
assert!(dbg.contains("boom"));
}
#[test]
fn error_trait_implemented() {
fn assert_error<E: std::error::Error>() {}
assert_error::<AiError>();
}
}