Skip to main content

hrpc/
proto.rs

1use std::{error::Error as StdError, fmt::Display, str::FromStr};
2
3use bytes::Bytes;
4
5use crate::{response::BoxResponse, BoxError, Response};
6
7crate::include_proto!("hrpc.v1");
8
9/// Represents a hRPC error identifier.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub enum HrpcErrorIdentifier {
12    /// Endpoint was not implemented by server.
13    NotImplemented,
14    /// Reached resource quota or rate limited by server.
15    ResourceExhausted,
16    /// An error occured in server.
17    InternalServerError,
18    /// The server could not be reached, most likely means the server is down.
19    Unavailable,
20    /// Specified endpoint was not found on server.
21    NotFound,
22}
23
24impl From<HrpcErrorIdentifier> for String {
25    fn from(id: HrpcErrorIdentifier) -> Self {
26        id.as_id().to_string()
27    }
28}
29
30impl AsRef<str> for HrpcErrorIdentifier {
31    fn as_ref(&self) -> &str {
32        self.as_id()
33    }
34}
35
36/// Error produced when trying to parse a string as a [`HrpcErrorIdentifier`].
37#[derive(Debug)]
38#[non_exhaustive]
39pub struct NotHrpcErrorIdentifier;
40
41impl FromStr for HrpcErrorIdentifier {
42    type Err = NotHrpcErrorIdentifier;
43
44    fn from_str(s: &str) -> Result<Self, Self::Err> {
45        compare_if_ret::compare_if_ret! {
46            s,
47            Self::NotImplemented,
48            Self::ResourceExhausted,
49            Self::InternalServerError,
50            Self::Unavailable,
51            Self::NotFound,
52        }
53    }
54}
55
56mod compare_if_ret {
57    macro_rules! compare_if_ret {
58        ($var:ident, $variant:expr, $( $variant2:expr, )+) => {
59            if $variant.compare($var) {
60                return Ok($variant);
61            } $(
62                else if $variant2.compare($var) {
63                    return Ok($variant2);
64                }
65            )* else {
66                return Err(NotHrpcErrorIdentifier);
67            }
68        };
69    }
70
71    pub(crate) use compare_if_ret;
72}
73
74impl HrpcErrorIdentifier {
75    /// Return the string version of this hRPC identifier.
76    pub const fn as_id(&self) -> &'static str {
77        match self {
78            Self::InternalServerError => "hrpc.internal-server-error",
79            Self::ResourceExhausted => "hrpc.resource-exhausted",
80            Self::NotImplemented => "hrpc.not-implemented",
81            Self::Unavailable => "hrpc.unavailable",
82            Self::NotFound => "hrpc.not-found",
83        }
84    }
85
86    /// Compare this hRPC identifier with some string identifier to see if they match.
87    pub fn compare(&self, identifier: impl AsRef<str>) -> bool {
88        identifier.as_ref() == self.as_id()
89    }
90}
91
92impl Error {
93    /// Create a new hRPC error representing a not implemented endpoint ([`HrpcErrorIdentifier::NotImplemented`]).
94    pub fn new_not_implemented(message: impl Into<String>) -> Self {
95        Self::default()
96            .with_identifier(HrpcErrorIdentifier::NotImplemented)
97            .with_message(message)
98    }
99
100    /// Create a new hRPC error representing resource exhaustion by a client ([`HrpcErrorIdentifier::ResourceExhausted`]).
101    pub fn new_resource_exhausted(message: impl Into<String>) -> Self {
102        Self::default()
103            .with_identifier(HrpcErrorIdentifier::ResourceExhausted)
104            .with_message(message)
105    }
106
107    /// Create a new hRPC error representing an internal server error ([`HrpcErrorIdentifier::InternalServerError`]).
108    pub fn new_internal_server_error(message: impl Into<String>) -> Self {
109        Self::default()
110            .with_identifier(HrpcErrorIdentifier::InternalServerError)
111            .with_message(message)
112    }
113
114    /// Create a new hRPC error representing a not found error ([`HrpcErrorIdentifier::NotFound`]).
115    pub fn new_not_found(message: impl Into<String>) -> Self {
116        Self::default()
117            .with_identifier(HrpcErrorIdentifier::NotFound)
118            .with_message(message)
119    }
120
121    /// Set the "more details" of this hRPC error.
122    pub fn with_details(mut self, details: impl Into<Bytes>) -> Self {
123        self.details = details.into();
124        self
125    }
126
127    /// Set the "identifier" of this hRPC error.
128    pub fn with_identifier(mut self, identifier: impl Into<String>) -> Self {
129        self.identifier = identifier.into();
130        self
131    }
132
133    /// Set the "human message" of this hRPC error.
134    pub fn with_message(mut self, message: impl Into<String>) -> Self {
135        self.human_message = message.into();
136        self
137    }
138
139    pub(crate) fn invalid_hrpc_error(details: impl Into<Bytes>) -> Self {
140        Self {
141            human_message:
142                "the server error was an invalid hRPC error, check more_details field for the error"
143                    .to_string(),
144            identifier: "hrpcrs.invalid-hrpc-error".to_string(),
145            details: details.into(),
146        }
147    }
148}
149
150impl<'a> From<&'a str> for Error {
151    fn from(msg: &'a str) -> Self {
152        Error::default()
153            .with_identifier(HrpcErrorIdentifier::InternalServerError)
154            .with_message(msg)
155    }
156}
157
158impl<'a, 'b> From<(&'a str, &'b str)> for Error {
159    fn from((id, msg): (&'a str, &'b str)) -> Self {
160        Error::default().with_identifier(id).with_message(msg)
161    }
162}
163
164impl From<String> for Error {
165    fn from(msg: String) -> Self {
166        Error::default()
167            .with_identifier(HrpcErrorIdentifier::InternalServerError)
168            .with_message(msg)
169    }
170}
171
172impl<'a> From<(&'a str, String)> for Error {
173    fn from((id, msg): (&'a str, String)) -> Self {
174        Error::default().with_identifier(id).with_message(msg)
175    }
176}
177
178impl From<BoxError> for Error {
179    fn from(err: BoxError) -> Self {
180        Error::default().with_message(err.to_string())
181    }
182}
183
184impl From<(&'static str, BoxError)> for Error {
185    fn from((id, err): (&'static str, BoxError)) -> Self {
186        Error::default()
187            .with_identifier(id)
188            .with_message(err.to_string())
189    }
190}
191
192impl From<Error> for Response<Error> {
193    fn from(err: Error) -> Self {
194        let mut resp = Response::new(&err);
195        resp.extensions_mut().insert(err);
196        resp
197    }
198}
199
200impl From<Error> for BoxResponse {
201    fn from(err: Error) -> Self {
202        Response::<Error>::from(err).map::<()>()
203    }
204}
205
206impl Display for Error {
207    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208        write!(f, "error '{}': {}", self.identifier, self.human_message)
209    }
210}
211
212impl StdError for Error {}