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#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub enum HrpcErrorIdentifier {
12 NotImplemented,
14 ResourceExhausted,
16 InternalServerError,
18 Unavailable,
20 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#[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 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 pub fn compare(&self, identifier: impl AsRef<str>) -> bool {
88 identifier.as_ref() == self.as_id()
89 }
90}
91
92impl Error {
93 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 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 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 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 pub fn with_details(mut self, details: impl Into<Bytes>) -> Self {
123 self.details = details.into();
124 self
125 }
126
127 pub fn with_identifier(mut self, identifier: impl Into<String>) -> Self {
129 self.identifier = identifier.into();
130 self
131 }
132
133 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 {}