1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4#[derive(Debug)]
6pub enum Error {
7 Config(String),
9 Provider { status: u16, body: String },
11 Json(serde_json::Error),
13 Internal(String),
15 Timeout,
17}
18
19impl fmt::Display for Error {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 Error::Config(msg) => write!(f, "config error: {msg}"),
23 Error::Provider { status, body } => {
24 write!(f, "provider error (HTTP {status}): {body}")
25 }
26 Error::Json(e) => write!(f, "json error: {e}"),
27 Error::Internal(msg) => write!(f, "internal error: {msg}"),
28 Error::Timeout => write!(f, "request timed out"),
29 }
30 }
31}
32
33impl Error {
34 pub fn is_transient(&self) -> bool {
38 match self {
39 Error::Provider { status, .. } => matches!(status, 429 | 500 | 502 | 503 | 504),
40 Error::Internal(_) | Error::Timeout => true,
41 _ => false,
42 }
43 }
44}
45
46impl std::error::Error for Error {
47 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
48 match self {
49 Error::Json(e) => Some(e),
50 _ => None,
51 }
52 }
53}
54
55#[cfg(feature = "gateway")]
56impl From<toml::de::Error> for Error {
57 fn from(e: toml::de::Error) -> Self {
58 Error::Config(e.to_string())
59 }
60}
61
62impl From<serde_json::Error> for Error {
63 fn from(e: serde_json::Error) -> Self {
64 Error::Json(e)
65 }
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct ApiError {
71 pub error: ApiErrorBody,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct ApiErrorBody {
76 pub message: String,
77 #[serde(rename = "type")]
78 pub kind: String,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub param: Option<String>,
81 #[serde(skip_serializing_if = "Option::is_none")]
82 pub code: Option<String>,
83}
84
85impl ApiError {
86 pub fn new(message: impl Into<String>, kind: impl Into<String>) -> Self {
87 ApiError {
88 error: ApiErrorBody {
89 message: message.into(),
90 kind: kind.into(),
91 param: None,
92 code: None,
93 },
94 }
95 }
96}