porkbun_api/
error.rs

1use hyper::http::{uri::InvalidUri, StatusCode};
2use serde::Deserialize;
3use serde_json::Error as JsonError;
4use std::fmt::Display;
5
6#[derive(Deserialize, Debug)]
7pub(crate) struct ApiErrorMessage {
8    message: String,
9}
10
11impl Display for ApiErrorMessage {
12    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13        self.message.fmt(f)
14    }
15}
16
17impl std::error::Error for ApiErrorMessage {}
18
19#[derive(Deserialize, Debug)]
20#[serde(tag = "status", rename_all = "UPPERCASE")]
21pub(crate) enum ApiResponse<T> {
22    Success(T),
23    Error(ApiErrorMessage),
24}
25
26impl<T> From<ApiResponse<T>> for std::result::Result<T, ApiErrorMessage> {
27    fn from(value: ApiResponse<T>) -> Self {
28        match value {
29            ApiResponse::Success(s) => Ok(s),
30            ApiResponse::Error(e) => Err(e),
31        }
32    }
33}
34
35/// The error returned when the upstream API server returns an error
36#[derive(Debug)]
37pub(crate) struct ApiError {
38    status: StatusCode,
39    error: Option<ApiErrorMessage>,
40}
41
42impl Display for ApiError {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        let Self { status, error } = self;
45        if let Some(error) = error {
46            f.write_fmt(format_args!("[{status}] {error}"))
47        } else {
48            f.write_fmt(format_args!("Invalid status code {status}"))
49        }
50    }
51}
52
53#[derive(Debug)]
54pub(crate) enum ErrorImpl<T: std::error::Error + Send + Sync + 'static> {
55    ApiError(ApiError),
56    TransportError(T),
57    SerializationError(JsonError),
58    DeserializationError(JsonError),
59    InvalidUri(InvalidUri),
60}
61
62impl<T: std::error::Error + Send + Sync + 'static> From<(StatusCode, Option<ApiErrorMessage>)>
63    for ErrorImpl<T>
64{
65    fn from(value: (StatusCode, Option<ApiErrorMessage>)) -> Self {
66        Self::ApiError(ApiError {
67            status: value.0,
68            error: value.1,
69        })
70    }
71}
72
73impl<T: std::error::Error + Send + Sync + 'static> From<hyper::http::uri::InvalidUri>
74    for ErrorImpl<T>
75{
76    fn from(value: hyper::http::uri::InvalidUri) -> Self {
77        Self::InvalidUri(value)
78    }
79}
80
81impl<T: std::error::Error + Send + Sync + 'static> Display for ErrorImpl<T> {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        match self {
84            Self::ApiError(api_error) => api_error.fmt(f),
85            Self::DeserializationError(_) => f.write_str("failed to deserialize response"),
86            Self::SerializationError(_) => f.write_str("failed to serialize request"),
87            Self::TransportError(_) => f.write_str("failed to send request or recieve response"),
88            Self::InvalidUri(_) => f.write_str("invalid uri"),
89        }
90    }
91}
92
93impl<T: std::error::Error + Send + Sync + 'static> std::error::Error for ErrorImpl<T> {
94    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
95        match self {
96            Self::ApiError { .. } => None,
97            Self::TransportError(t) => Some(t),
98            Self::SerializationError(s) | Self::DeserializationError(s) => Some(s),
99            Self::InvalidUri(u) => Some(u),
100        }
101    }
102}
103
104/// The error type that can be returned by the [Client](crate::Client).
105///
106/// The type `T` is the error type of the transport layer. That is, it is `<Transport as MakeRequest>::Error` for `Client<Transport>`.
107pub struct Error<T: std::error::Error + Send + Sync + 'static>(ErrorImpl<T>);
108
109impl<T: std::error::Error + Send + Sync + 'static> Error<T> {
110    /// Try to turn this error into an underlying transport error, by reference.
111    /// will return `None` if the error was not caused by the transport layer, but was instead a protocol level error.
112    pub fn as_transport_error(&self) -> Option<&T> {
113        if let ErrorImpl::TransportError(e) = &self.0 {
114            Some(e)
115        } else {
116            None
117        }
118    }
119    /// Try to turn this error into an underlying transport error, consuming the error.
120    /// will return `None` if the error was not caused by the transport layer, but was instead a protocol level error.
121    pub fn into_transport_error(self) -> Option<T> {
122        if let ErrorImpl::TransportError(e) = self.0 {
123            Some(e)
124        } else {
125            None
126        }
127    }
128}
129
130impl<T: std::error::Error + Send + Sync + 'static> std::fmt::Debug for Error<T> {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        std::fmt::Debug::fmt(&self.0, f)
133    }
134}
135
136impl<T: std::error::Error + Send + Sync + 'static> Display for Error<T> {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        std::fmt::Display::fmt(&self.0, f)
139    }
140}
141
142impl<T: std::error::Error + Send + Sync + 'static> std::error::Error for Error<T> {
143    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
144        self.0.source()
145    }
146}
147
148impl<E: std::error::Error + Send + Sync + 'static, T> From<T> for Error<E>
149where
150    T: Into<ErrorImpl<E>>,
151{
152    fn from(value: T) -> Self {
153        Self(value.into())
154    }
155}