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 create JSON body: {}", source)]
21 Json {
22 #[from]
23 source: serde_json::Error,
24 },
25}
26
27#[derive(Debug, Error)]
29#[non_exhaustive]
30pub enum ApiError<E>
31where
32 E: Error + Send + Sync + 'static,
33{
34 #[error("client error: {}", source)]
36 Client {
37 source: E,
39 },
40 #[error("failed to parse url: {}", source)]
42 UrlParse {
43 #[from]
45 source: url::ParseError,
46 },
47 #[error("failed to create form data: {}", source)]
49 Body {
50 #[from]
52 source: BodyError,
53 },
54 #[error("could not parse JSON response: {}", source)]
56 Json {
57 #[from]
59 source: serde_json::Error,
60 },
61 #[error("server responded with error: {}", msg)]
63 Server {
64 msg: String,
66 },
67 #[error("server responded with error: {} - {}", .status, String::from_utf8_lossy(.data))]
69 ServerService {
70 status: http::StatusCode,
72 data: Vec<u8>,
74 },
75 #[error("could not parse {} data from JSON: {}", typename, source)]
77 DataType {
78 source: serde_json::Error,
80 typename: &'static str,
82 },
83}
84
85impl<E> ApiError<E>
86where
87 E: Error + Send + Sync + 'static,
88{
89 pub fn client(source: E) -> Self {
91 ApiError::Client { source }
92 }
93
94 pub fn map_client<F, W>(self, f: F) -> ApiError<W>
96 where
97 F: FnOnce(E) -> W,
98 W: Error + Send + Sync + 'static,
99 {
100 match self {
101 Self::Client { source } => ApiError::client(f(source)),
102 Self::UrlParse { source } => ApiError::UrlParse { source },
103 Self::Body { source } => ApiError::Body { source },
104 Self::Json { source } => ApiError::Json { source },
105 Self::Server { msg } => ApiError::Server { msg },
106 Self::ServerService { status, data } => ApiError::ServerService { status, data },
107 Self::DataType { source, typename } => ApiError::DataType { source, typename },
108 }
109 }
110
111 pub(crate) fn server_error(status: http::StatusCode, body: &bytes::Bytes) -> Self {
112 Self::ServerService {
113 status,
114 data: body.into_iter().copied().collect(),
115 }
116 }
117
118 pub(crate) fn data_type<T>(source: serde_json::Error) -> Self {
119 ApiError::DataType {
120 source,
121 typename: any::type_name::<T>(),
122 }
123 }
124}