http_request_derive/
error.rs

1// SPDX-FileCopyrightText: OpenTalk GmbH <mail@opentalk.eu>
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5use bytes::Bytes;
6use http::{StatusCode, uri::InvalidUri};
7use snafu::{Location, Snafu};
8use url::Url;
9
10/// Errors that originate from this crate
11#[derive(Debug, Snafu)]
12#[snafu(visibility(pub(crate)))]
13pub enum Error {
14    /// Encountered a non-success http status code that was not handled otherwise
15    #[snafu(display("server returned a non-success http status code {status}"))]
16    NonSuccessStatus {
17        /// The returned status code.
18        status: StatusCode,
19
20        /// The body returned from the request
21        body: Bytes,
22    },
23
24    /// An error occurred when building a HTTP request
25    #[snafu(display("could not build http request: {source}"))]
26    BuildRequest {
27        /// The source http error
28        source: http::Error,
29    },
30
31    /// Encountered a URL which cannot be a base where a base url was required
32    #[snafu(display("base url {url} cannot be a base"))]
33    UrlCannotBeABase {
34        /// The url which cannot be a base
35        url: Url,
36    },
37
38    /// Couldn't parse a HTTP URI
39    #[snafu(display("couldn't parse uri"))]
40    ParseUri {
41        /// The source invalid uri error
42        source: InvalidUri,
43    },
44
45    /// A query string couldn't be created from the given type
46    #[snafu(display("can't create query string: {message}"))]
47    QueryString {
48        /// A message describing the reason for this error
49        message: String,
50    },
51
52    /// Couldn't create a query from a given string
53    #[cfg(feature = "serde")]
54    #[snafu(display("couldn't build query string from serde url params"))]
55    SerdeUrlParams {
56        /// The source of the serde_url_params error
57        source: serde_url_params::Error,
58    },
59
60    /// serde_json error
61    #[cfg(feature = "serde")]
62    #[snafu(display("serde json error"))]
63    Json {
64        /// The source of the serde_json error
65        source: serde_json::Error,
66    },
67
68    /// custom error returned e.g. by a custom trait implementation in a different crate
69    #[snafu(display("{message}"))]
70    Custom {
71        /// The custom error message
72        message: String,
73
74        /// The location where the error happened
75        location: Location,
76    },
77}
78
79impl Error {
80    /// Create a custom error
81    #[track_caller]
82    pub fn custom(message: String) -> Self {
83        let location = std::panic::Location::caller();
84
85        Error::Custom {
86            message,
87            location: Location::new(location.file(), location.line(), location.column()),
88        }
89    }
90
91    /// Query whether the error is caused by an HTTP Unauthorized status code
92    pub const fn is_unauthorized(&self) -> bool {
93        self.is_specific_http_error_status(&StatusCode::UNAUTHORIZED)
94    }
95
96    /// Query whether the error is caused by a HTTP NotFound status code
97    pub const fn is_not_found(&self) -> bool {
98        self.is_specific_http_error_status(&StatusCode::NOT_FOUND)
99    }
100
101    /// Query whether the error is caused by a HTTP BadRequest status code
102    pub const fn is_bad_request(&self) -> bool {
103        self.is_specific_http_error_status(&StatusCode::BAD_REQUEST)
104    }
105
106    /// Query whether the error is caused by a HTTP InternalServerError status code
107    pub const fn is_internal_server_error(&self) -> bool {
108        self.is_specific_http_error_status(&StatusCode::INTERNAL_SERVER_ERROR)
109    }
110
111    /// Query whether the error is caused by a non-success HTTP status code
112    pub const fn is_http_error_status(&self) -> bool {
113        matches!(self, Self::NonSuccessStatus { .. })
114    }
115
116    /// Query whether the error is caused by a specific HTTP status code
117    pub const fn is_specific_http_error_status(&self, specific_status: &StatusCode) -> bool {
118        matches!(self, Self::NonSuccessStatus { status,.. } if status.as_u16() == specific_status.as_u16())
119    }
120}