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    #[error("failed to create JSON body: {}", source)]
21    Json {
22        #[from]
23        source: serde_json::Error,
24    },
25}
26
27/// Errors which may occur when using API endpoints.
28#[derive(Debug, Error)]
29#[non_exhaustive]
30pub enum ApiError<E>
31where
32    E: Error + Send + Sync + 'static,
33{
34    /// The client encountered an error.
35    #[error("client error: {}", source)]
36    Client {
37        /// The client error.
38        source: E,
39    },
40    /// The URL failed to parse.
41    #[error("failed to parse url: {}", source)]
42    UrlParse {
43        /// The source of the error.
44        #[from]
45        source: url::ParseError,
46    },
47    /// Body data could not be created.
48    #[error("failed to create form data: {}", source)]
49    Body {
50        /// The source of the error.
51        #[from]
52        source: BodyError,
53    },
54    /// JSON deserialization from API failed.
55    #[error("could not parse JSON response: {}", source)]
56    Json {
57        /// The source of the error.
58        #[from]
59        source: serde_json::Error,
60    },
61    /// Server returned an error message.
62    #[error("server responded with error: {}", msg)]
63    Server {
64        /// The error message from the server.
65        msg: String,
66    },
67    /// Server returned an error without JSON information.
68    #[error("server responded with error: {} - {}", .status, String::from_utf8_lossy(.data))]
69    ServerService {
70        /// The status code for the return.
71        status: http::StatusCode,
72        /// The error data from the server.
73        data: Vec<u8>,
74    },
75    /// Failed to parse an expected data type from JSON.
76    #[error("could not parse {} data from JSON: {}", typename, source)]
77    DataType {
78        /// The source of the error.
79        source: serde_json::Error,
80        /// The name of the type that could not be deserialized.
81        typename: &'static str,
82    },
83}
84
85impl<E> ApiError<E>
86where
87    E: Error + Send + Sync + 'static,
88{
89    /// Create an API error in a client error.
90    pub fn client(source: E) -> Self {
91        ApiError::Client { source }
92    }
93
94    /// Wrap a client error in another wrapper.
95    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}