gen_api_wrapper/
error.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6//
7// Originally from https://gitlab.kitware.com/utils/rust-gitlab
8//
9// Modified in an attempt to make it general beyond just gitlab
10
11use std::any;
12use std::error::Error;
13
14use thiserror::Error;
15
16/// Errors which may occur when creating form data.
17#[derive(Debug, Error)]
18#[non_exhaustive]
19pub enum BodyError {
20    /// Body data could not be serialized from form parameters.
21    #[error("failed to URL encode form parameters: {}", source)]
22    UrlEncoded {
23        /// The source of the error.
24        #[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/// Errors which may occur when using API endpoints.
36#[derive(Debug, Error)]
37#[non_exhaustive]
38pub enum ApiError<E>
39where
40    E: Error + Send + Sync + 'static,
41{
42    /// The client encountered an error.
43    #[error("client error: {}", source)]
44    Client {
45        /// The client error.
46        source: E,
47    },
48    /// The URL failed to parse.
49    #[error("failed to parse url: {}", source)]
50    UrlParse {
51        /// The source of the error.
52        #[from]
53        source: url::ParseError,
54    },
55    /// Body data could not be created.
56    #[error("failed to create form data: {}", source)]
57    Body {
58        /// The source of the error.
59        #[from]
60        source: BodyError,
61    },
62    /// JSON deserialization from API failed.
63    #[error("could not parse JSON response: {}", source)]
64    Json {
65        /// The source of the error.
66        #[from]
67        source: serde_json::Error,
68    },
69    /// Server returned an error message.
70    #[error("server responded with error: {}", msg)]
71    Server {
72        /// The error message from the server.
73        msg: String,
74    },
75    /// Server returned an error without JSON information.
76    #[error("server responded with error: {} - {}", .status, String::from_utf8_lossy(.data))]
77    ServerService {
78        /// The status code for the return.
79        status: http::StatusCode,
80        /// The error data from the server.
81        data: Vec<u8>,
82    },
83    /// Failed to parse an expected data type from JSON.
84    #[error("could not parse {} data from JSON: {}", typename, source)]
85    DataType {
86        /// The source of the error.
87        source: serde_json::Error,
88        /// The name of the type that could not be deserialized.
89        typename: &'static str,
90    },
91}
92
93impl<E> ApiError<E>
94where
95    E: Error + Send + Sync + 'static,
96{
97    /// Create an API error in a client error.
98    pub fn client(source: E) -> Self {
99        ApiError::Client { source }
100    }
101
102    /// Wrap a client error in another wrapper.
103    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}