1use std::any;
12use std::error::Error;
13
14use thiserror::Error;
15
16#[derive(Debug, Error)]
18#[non_exhaustive]
19pub enum BodyError {
20 #[error("failed to URL encode form parameters: {}", source)]
22 UrlEncoded {
23 #[from]
25 source: serde_urlencoded::ser::Error,
26 },
27
28 #[error("failed to create JSON body: {}", source)]
29 Json {
30 #[from]
31 source: serde_json::Error,
32 },
33}
34
35#[derive(Debug, Error)]
37#[non_exhaustive]
38pub enum ApiError<E>
39where
40 E: Error + Send + Sync + 'static,
41{
42 #[error("client error: {}", source)]
44 Client {
45 source: E,
47 },
48 #[error("failed to parse url: {}", source)]
50 UrlParse {
51 #[from]
53 source: url::ParseError,
54 },
55 #[error("failed to create form data: {}", source)]
57 Body {
58 #[from]
60 source: BodyError,
61 },
62 #[error("could not parse JSON response: {}", source)]
64 Json {
65 #[from]
67 source: serde_json::Error,
68 },
69 #[error("server responded with error: {}", msg)]
71 Server {
72 msg: String,
74 },
75 #[error("server responded with error: {} - {}", .status, String::from_utf8_lossy(.data))]
77 ServerService {
78 status: http::StatusCode,
80 data: Vec<u8>,
82 },
83 #[error("could not parse {} data from JSON: {}", typename, source)]
85 DataType {
86 source: serde_json::Error,
88 typename: &'static str,
90 },
91}
92
93impl<E> ApiError<E>
94where
95 E: Error + Send + Sync + 'static,
96{
97 pub fn client(source: E) -> Self {
99 ApiError::Client { source }
100 }
101
102 pub fn map_client<F, W>(self, f: F) -> ApiError<W>
104 where
105 F: FnOnce(E) -> W,
106 W: Error + Send + Sync + 'static,
107 {
108 match self {
109 Self::Client { source } => ApiError::client(f(source)),
110 Self::UrlParse { source } => ApiError::UrlParse { source },
111 Self::Body { source } => ApiError::Body { source },
112 Self::Json { source } => ApiError::Json { source },
113 Self::Server { msg } => ApiError::Server { msg },
114 Self::ServerService { status, data } => ApiError::ServerService { status, data },
115 Self::DataType { source, typename } => ApiError::DataType { source, typename },
116 }
117 }
118
119 pub(crate) fn server_error(status: http::StatusCode, body: &bytes::Bytes) -> Self {
120 Self::ServerService {
121 status,
122 data: body.into_iter().copied().collect(),
123 }
124 }
125
126 pub(crate) fn data_type<T>(source: serde_json::Error) -> Self {
127 ApiError::DataType {
128 source,
129 typename: any::type_name::<T>(),
130 }
131 }
132}