1use crate::auth::AuthError;
2use thiserror::Error;
3
4#[derive(Debug, Error)]
5pub enum Error {
6 #[error("Configuration error: {0}")]
7 Config(String),
8
9 #[error("Schema validation failed: {0}")]
10 SchemaValidation(String),
11
12 #[error("Provider error ({provider}): {kind:?}")]
13 Provider {
14 provider: String,
15 kind: ProviderErrorKind,
16 },
17
18 #[error("Transform error: {0}")]
19 Transform(String),
20
21 #[error("Strict mode violation")]
22 StrictModeViolation,
23
24 #[error("Circuit breaker is open")]
25 CircuitBreakerOpen,
26
27 #[error("Circuit breaker is half-open, rejecting request")]
28 CircuitBreakerHalfOpen,
29
30 #[error("HTTP error: {0}")]
31 Http(#[from] reqwest::Error),
32
33 #[error("JSON error: {0}")]
34 Json(#[from] serde_json::Error),
35
36 #[error("IO error: {0}")]
37 Io(#[from] std::io::Error),
38
39 #[error("Authentication error: {0}")]
40 Auth(#[from] AuthError),
41}
42
43#[derive(Debug, Clone, PartialEq, Eq)]
44pub enum ProviderErrorKind {
45 RateLimit,
46 Timeout,
47 InvalidRequest,
48 AuthenticationFailed,
49 ServerError,
50 Unknown,
51}
52
53pub type Result<T> = std::result::Result<T, Error>;
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use crate::auth::AuthError;
59
60 #[test]
61 fn wraps_io_error() {
62 let err = std::io::Error::other("disk failed");
63 let wrapped = Error::from(err);
64 matches!(wrapped, Error::Io(_))
65 .then_some(())
66 .expect("io errors map to Error::Io");
67 }
68
69 #[test]
70 fn wraps_json_error() {
71 let err = serde_json::from_str::<serde_json::Value>("not-json").unwrap_err();
72 let wrapped = Error::from(err);
73 assert!(matches!(wrapped, Error::Json(_)));
74 }
75
76 #[test]
77 fn auth_error_converts() {
78 let auth_err = AuthError::MissingEnvVar("OPENAI_API_KEY".into());
79 let wrapped = Error::from(auth_err);
80 assert!(matches!(wrapped, Error::Auth(_)));
81 }
82
83 #[test]
84 fn provider_error_kind_debug() {
85 let kind = ProviderErrorKind::RateLimit;
86 assert_eq!(format!("{:?}", kind), "RateLimit");
87 }
88}