use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug)]
pub enum Error {
Config(String),
Provider { status: u16, body: String },
Json(serde_json::Error),
Internal(String),
Timeout,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Config(msg) => write!(f, "config error: {msg}"),
Error::Provider { status, body } => {
write!(f, "provider error (HTTP {status}): {body}")
}
Error::Json(e) => write!(f, "json error: {e}"),
Error::Internal(msg) => write!(f, "internal error: {msg}"),
Error::Timeout => write!(f, "request timed out"),
}
}
}
impl Error {
pub fn is_transient(&self) -> bool {
match self {
Error::Provider { status, .. } => matches!(status, 429 | 500 | 502 | 503 | 504),
Error::Internal(_) | Error::Timeout => true,
_ => false,
}
}
pub fn not_implemented(method: &str) -> Self {
Error::Internal(format!("provider method '{method}' not implemented"))
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Json(e) => Some(e),
_ => None,
}
}
}
#[cfg(feature = "gateway")]
impl From<toml::de::Error> for Error {
fn from(e: toml::de::Error) -> Self {
Error::Config(e.to_string())
}
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
Error::Json(e)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct ApiError {
pub error: ApiErrorBody,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct ApiErrorBody {
pub message: String,
#[serde(rename = "type")]
pub kind: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub param: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
}
impl ApiError {
pub fn new(message: impl Into<String>, kind: impl Into<String>) -> Self {
ApiError {
error: ApiErrorBody {
message: message.into(),
kind: kind.into(),
param: None,
code: None,
},
}
}
}