use std::fmt;
#[derive(Debug, Clone)]
pub enum SamvadSetuError {
Http { status: u16, body: String },
Provider {
error_type: String,
message: String,
param: Option<String>,
code: Option<String>,
},
RateLimit {
retry_after_secs: Option<u64>,
message: String,
},
Parse {
message: String,
raw_response: Option<String>,
},
Config(String),
Auth(String),
Timeout,
Network(String),
BatchNotComplete { batch_id: String, status: String },
UnsupportedFeature { provider: String, feature: String },
}
impl fmt::Display for SamvadSetuError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Http { status, body } => write!(f, "HTTP {status}: {body}"),
Self::Provider { error_type, message, code, param } => {
write!(f, "Provider error [{error_type}]")?;
if let Some(c) = code {
write!(f, " (code: {c})")?;
}
if let Some(p) = param {
write!(f, " (param: {p})")?;
}
write!(f, ": {message}")
}
Self::RateLimit { retry_after_secs, message } => match retry_after_secs {
Some(s) => write!(f, "Rate limited (retry after {s}s): {message}"),
None => write!(f, "Rate limited: {message}"),
},
Self::Parse { message, .. } => write!(f, "Parse error: {message}"),
Self::Config(msg) => write!(f, "Config error: {msg}"),
Self::Auth(msg) => write!(f, "Auth error: {msg}"),
Self::Timeout => write!(f, "Request timed out"),
Self::Network(msg) => write!(f, "Network error: {msg}"),
Self::BatchNotComplete { batch_id, status } => {
write!(f, "Batch {batch_id} not complete (status: {status})")
}
Self::UnsupportedFeature { provider, feature } => {
write!(f, "{provider} does not support {feature}")
}
}
}
}
impl std::error::Error for SamvadSetuError {}
pub(crate) fn from_reqwest_error(e: reqwest::Error) -> SamvadSetuError {
if e.is_timeout() {
SamvadSetuError::Timeout
} else if e.is_connect() {
SamvadSetuError::Network(format!("Connection failed: {e}"))
} else {
SamvadSetuError::Network(e.to_string())
}
}
pub(crate) fn parse_openai_error_body(
status: u16,
body: &str,
) -> SamvadSetuError {
if let Ok(val) = serde_json::from_str::<serde_json::Value>(body)
&& let Some(err) = val.get("error")
{
return SamvadSetuError::Provider {
error_type: err
.get("type")
.and_then(|v| v.as_str())
.unwrap_or("unknown")
.to_string(),
message: err
.get("message")
.and_then(|v| v.as_str())
.unwrap_or(body)
.to_string(),
param: err.get("param").and_then(|v| v.as_str()).map(str::to_string),
code: err.get("code").and_then(|v| v.as_str()).map(str::to_string),
};
}
SamvadSetuError::Http { status, body: body.to_string() }
}
pub(crate) fn parse_anthropic_error_body(status: u16, body: &str) -> SamvadSetuError {
if let Ok(val) = serde_json::from_str::<serde_json::Value>(body)
&& let Some(err) = val.get("error")
{
return SamvadSetuError::Provider {
error_type: err
.get("type")
.and_then(|v| v.as_str())
.unwrap_or("api_error")
.to_string(),
message: err
.get("message")
.and_then(|v| v.as_str())
.unwrap_or(body)
.to_string(),
param: None,
code: None,
};
}
SamvadSetuError::Http { status, body: body.to_string() }
}