use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StandardErrorCode {
InvalidRequest,
Authentication,
PermissionDenied,
NotFound,
RequestTooLarge,
RateLimited,
QuotaExhausted,
ServerError,
Overloaded,
Timeout,
Conflict,
Cancelled,
Unknown,
}
impl StandardErrorCode {
#[inline]
pub fn code(&self) -> &'static str {
match self {
Self::InvalidRequest => "E1001",
Self::Authentication => "E1002",
Self::PermissionDenied => "E1003",
Self::NotFound => "E1004",
Self::RequestTooLarge => "E1005",
Self::RateLimited => "E2001",
Self::QuotaExhausted => "E2002",
Self::ServerError => "E3001",
Self::Overloaded => "E3002",
Self::Timeout => "E3003",
Self::Conflict => "E4001",
Self::Cancelled => "E4002",
Self::Unknown => "E9999",
}
}
#[inline]
pub fn name(&self) -> &'static str {
match self {
Self::InvalidRequest => "invalid_request",
Self::Authentication => "authentication",
Self::PermissionDenied => "permission_denied",
Self::NotFound => "not_found",
Self::RequestTooLarge => "request_too_large",
Self::RateLimited => "rate_limited",
Self::QuotaExhausted => "quota_exhausted",
Self::ServerError => "server_error",
Self::Overloaded => "overloaded",
Self::Timeout => "timeout",
Self::Conflict => "conflict",
Self::Cancelled => "cancelled",
Self::Unknown => "unknown",
}
}
#[inline]
pub fn retryable(&self) -> bool {
matches!(
self,
Self::RateLimited
| Self::ServerError
| Self::Overloaded
| Self::Timeout
| Self::Conflict
)
}
#[inline]
pub fn fallbackable(&self) -> bool {
matches!(
self,
Self::Authentication
| Self::RateLimited
| Self::QuotaExhausted
| Self::ServerError
| Self::Overloaded
| Self::Timeout
)
}
#[inline]
pub fn category(&self) -> &'static str {
match self {
Self::InvalidRequest
| Self::Authentication
| Self::PermissionDenied
| Self::NotFound
| Self::RequestTooLarge => "client",
Self::RateLimited | Self::QuotaExhausted => "rate",
Self::ServerError | Self::Overloaded | Self::Timeout => "server",
Self::Conflict | Self::Cancelled => "operational",
Self::Unknown => "unknown",
}
}
pub fn from_provider_code(provider_code: &str) -> Option<Self> {
let code = match provider_code {
"invalid_request" | "invalid_request_error" => Self::InvalidRequest,
"authentication" | "authorized_error" | "invalid_api_key" | "authentication_error" => {
Self::Authentication
}
"permission_denied" | "permission_error" => Self::PermissionDenied,
"not_found" | "model_not_found" => Self::NotFound,
"request_too_large" | "context_length_exceeded" => Self::RequestTooLarge,
"rate_limited" | "rate_limit_exceeded" => Self::RateLimited,
"quota_exhausted" | "insufficient_quota" => Self::QuotaExhausted,
"server_error" => Self::ServerError,
"overloaded" | "overloaded_error" => Self::Overloaded,
"timeout" => Self::Timeout,
"conflict" => Self::Conflict,
"cancelled" => Self::Cancelled,
_ => return None,
};
Some(code)
}
pub fn from_error_class(error_class: &str) -> Self {
Self::from_provider_code(error_class).unwrap_or(Self::Unknown)
}
pub fn from_http_status(status: u16) -> Self {
match status {
400 => Self::InvalidRequest,
401 => Self::Authentication,
403 => Self::PermissionDenied,
404 => Self::NotFound,
408 => Self::Timeout,
409 => Self::Conflict,
413 => Self::RequestTooLarge,
429 => Self::RateLimited, 500 => Self::ServerError,
503 => Self::Overloaded,
504 => Self::Timeout,
529 => Self::Overloaded, _ => Self::Unknown,
}
}
#[inline]
pub fn from_standard_code(s: &str) -> Option<Self> {
match s {
"E1001" => Some(Self::InvalidRequest),
"E1002" => Some(Self::Authentication),
"E1003" => Some(Self::PermissionDenied),
"E1004" => Some(Self::NotFound),
"E1005" => Some(Self::RequestTooLarge),
"E2001" => Some(Self::RateLimited),
"E2002" => Some(Self::QuotaExhausted),
"E3001" => Some(Self::ServerError),
"E3002" => Some(Self::Overloaded),
"E3003" => Some(Self::Timeout),
"E4001" => Some(Self::Conflict),
"E4002" => Some(Self::Cancelled),
"E9999" => Some(Self::Unknown),
_ => None,
}
}
}
impl Serialize for StandardErrorCode {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.code())
}
}
impl<'de> Deserialize<'de> for StandardErrorCode {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
Self::from_standard_code(&s)
.ok_or_else(|| serde::de::Error::custom(format!("unknown standard error code: {s}")))
}
}
impl fmt::Display for StandardErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.code())
}
}