1use std::fmt;
4
5#[derive(Debug, Clone)]
7pub enum SamvadSetuError {
8 Http { status: u16, body: String },
10
11 Provider {
13 error_type: String,
15 message: String,
17 param: Option<String>,
19 code: Option<String>,
21 },
22
23 RateLimit {
25 retry_after_secs: Option<u64>,
27 message: String,
28 },
29
30 Parse {
32 message: String,
33 raw_response: Option<String>,
35 },
36
37 Config(String),
39
40 Auth(String),
42
43 Timeout,
45
46 Network(String),
48
49 BatchNotComplete { batch_id: String, status: String },
51
52 UnsupportedFeature { provider: String, feature: String },
54}
55
56impl fmt::Display for SamvadSetuError {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 match self {
59 Self::Http { status, body } => write!(f, "HTTP {status}: {body}"),
60 Self::Provider { error_type, message, code, param } => {
61 write!(f, "Provider error [{error_type}]")?;
62 if let Some(c) = code {
63 write!(f, " (code: {c})")?;
64 }
65 if let Some(p) = param {
66 write!(f, " (param: {p})")?;
67 }
68 write!(f, ": {message}")
69 }
70 Self::RateLimit { retry_after_secs, message } => match retry_after_secs {
71 Some(s) => write!(f, "Rate limited (retry after {s}s): {message}"),
72 None => write!(f, "Rate limited: {message}"),
73 },
74 Self::Parse { message, .. } => write!(f, "Parse error: {message}"),
75 Self::Config(msg) => write!(f, "Config error: {msg}"),
76 Self::Auth(msg) => write!(f, "Auth error: {msg}"),
77 Self::Timeout => write!(f, "Request timed out"),
78 Self::Network(msg) => write!(f, "Network error: {msg}"),
79 Self::BatchNotComplete { batch_id, status } => {
80 write!(f, "Batch {batch_id} not complete (status: {status})")
81 }
82 Self::UnsupportedFeature { provider, feature } => {
83 write!(f, "{provider} does not support {feature}")
84 }
85 }
86 }
87}
88
89impl std::error::Error for SamvadSetuError {}
90
91pub(crate) fn from_reqwest_error(e: reqwest::Error) -> SamvadSetuError {
93 if e.is_timeout() {
94 SamvadSetuError::Timeout
95 } else if e.is_connect() {
96 SamvadSetuError::Network(format!("Connection failed: {e}"))
97 } else {
98 SamvadSetuError::Network(e.to_string())
99 }
100}
101
102pub(crate) fn parse_openai_error_body(
105 status: u16,
106 body: &str,
107) -> SamvadSetuError {
108 if let Ok(val) = serde_json::from_str::<serde_json::Value>(body)
109 && let Some(err) = val.get("error")
110 {
111 return SamvadSetuError::Provider {
112 error_type: err
113 .get("type")
114 .and_then(|v| v.as_str())
115 .unwrap_or("unknown")
116 .to_string(),
117 message: err
118 .get("message")
119 .and_then(|v| v.as_str())
120 .unwrap_or(body)
121 .to_string(),
122 param: err.get("param").and_then(|v| v.as_str()).map(str::to_string),
123 code: err.get("code").and_then(|v| v.as_str()).map(str::to_string),
124 };
125 }
126 SamvadSetuError::Http { status, body: body.to_string() }
127}
128
129pub(crate) fn parse_anthropic_error_body(status: u16, body: &str) -> SamvadSetuError {
132 if let Ok(val) = serde_json::from_str::<serde_json::Value>(body)
133 && let Some(err) = val.get("error")
134 {
135 return SamvadSetuError::Provider {
136 error_type: err
137 .get("type")
138 .and_then(|v| v.as_str())
139 .unwrap_or("api_error")
140 .to_string(),
141 message: err
142 .get("message")
143 .and_then(|v| v.as_str())
144 .unwrap_or(body)
145 .to_string(),
146 param: None,
147 code: None,
148 };
149 }
150 SamvadSetuError::Http { status, body: body.to_string() }
151}