1use std::collections::HashMap;
4
5type ErrorDatum = Box<dyn erased_serde::Serialize + Send + 'static>;
7
8#[derive(serde::Serialize)]
10pub struct RpcError {
11 message: String,
13 #[serde(serialize_with = "ser_code")]
15 code: RpcErrorKind,
16 #[serde(serialize_with = "ser_kind")]
18 kinds: AnyErrorKind,
19 #[serde(skip_serializing_if = "Option::is_none")]
21 data: Option<HashMap<String, ErrorDatum>>,
22}
23
24impl RpcError {
25 pub fn new(message: String, code: RpcErrorKind) -> Self {
27 Self {
28 message,
29 code,
30 kinds: AnyErrorKind::Rpc(code),
31 data: None,
32 }
33 }
34
35 pub fn set_kind(&mut self, kind: tor_error::ErrorKind) {
37 self.kinds = AnyErrorKind::Tor(kind);
38 }
39
40 pub fn set_datum<D>(
45 &mut self,
46 keyword: String,
47 datum: D,
48 ) -> Result<(), crate::InvalidRpcIdentifier>
49 where
50 D: serde::Serialize + Send + 'static,
51 {
52 crate::is_valid_rpc_identifier(None, &keyword)?;
53 self.data
54 .get_or_insert_with(HashMap::new)
55 .insert(keyword, Box::new(datum) as _);
56
57 Ok(())
58 }
59
60 pub fn is_internal(&self) -> bool {
62 matches!(
63 self.kinds,
64 AnyErrorKind::Tor(tor_error::ErrorKind::Internal)
65 | AnyErrorKind::Rpc(RpcErrorKind::InternalError)
66 )
67 }
68}
69
70impl<T> From<T> for RpcError
71where
72 T: std::error::Error + tor_error::HasKind + Send + 'static,
73{
74 fn from(value: T) -> RpcError {
75 use tor_error::ErrorReport as _;
76 let message = value.report().to_string();
77 let code = kind_to_code(value.kind());
78 let kinds = AnyErrorKind::Tor(value.kind());
79 RpcError {
80 message,
81 code,
82 kinds,
83 data: None,
84 }
85 }
86}
87
88fn ser_kind<S: serde::Serializer>(kind: &AnyErrorKind, s: S) -> Result<S::Ok, S::Error> {
90 use serde::ser::SerializeSeq;
94 let mut seq = s.serialize_seq(None)?;
95 match kind {
96 AnyErrorKind::Tor(kind) => seq.serialize_element(&format!("arti:{:?}", kind))?,
97 AnyErrorKind::Rpc(kind) => seq.serialize_element(&format!("rpc:{:?}", kind))?,
98 }
99 seq.end()
100}
101
102fn ser_code<S: serde::Serializer>(kind: &RpcErrorKind, s: S) -> Result<S::Ok, S::Error> {
104 s.serialize_i32(*kind as i32)
105}
106
107#[derive(Clone, Copy, Debug)]
109enum AnyErrorKind {
110 Tor(tor_error::ErrorKind),
112 #[allow(unused)]
114 Rpc(RpcErrorKind),
115}
116
117#[derive(Clone, Copy, Debug, Eq, PartialEq)]
126#[repr(i32)]
127#[non_exhaustive]
128pub enum RpcErrorKind {
129 InvalidRequest = -32600,
131 NoSuchMethod = -32601,
133 InvalidMethodParameters = -32602,
135 InternalError = -32603,
137 ObjectNotFound = 1,
139 RequestError = 2,
141 MethodNotImpl = 3,
143 RequestCancelled = 4,
145 FeatureNotPresent = 5,
147 WeakReferenceExpired = 6,
149}
150
151fn kind_to_code(kind: tor_error::ErrorKind) -> RpcErrorKind {
156 use RpcErrorKind as RC;
157 use tor_error::ErrorKind as EK;
158 match kind {
159 EK::Internal | EK::BadApiUsage => RC::InternalError,
160 _ => RC::RequestError, }
162}
163
164impl std::fmt::Debug for RpcError {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 f.debug_struct("RpcError")
167 .field("message", &self.message)
168 .field("code", &self.code)
169 .field("kinds", &self.kinds)
170 .finish()
171 }
172}
173
174#[cfg(test)]
175mod test {
176 #![allow(clippy::bool_assert_comparison)]
178 #![allow(clippy::clone_on_copy)]
179 #![allow(clippy::dbg_macro)]
180 #![allow(clippy::mixed_attributes_style)]
181 #![allow(clippy::print_stderr)]
182 #![allow(clippy::print_stdout)]
183 #![allow(clippy::single_char_pattern)]
184 #![allow(clippy::unwrap_used)]
185 #![allow(clippy::unchecked_time_subtraction)]
186 #![allow(clippy::useless_vec)]
187 #![allow(clippy::needless_pass_by_value)]
188 use super::*;
191
192 #[derive(Debug, thiserror::Error, serde::Serialize)]
193 enum ExampleError {
194 #[error("The {} exploded because {}", what, why)]
195 SomethingExploded { what: String, why: String },
196
197 #[error("I'm hiding the {0} in my {1}")]
198 SomethingWasHidden(String, String),
199
200 #[error("The {0} was missing")]
201 SomethingWasMissing(String),
202
203 #[error("I don't feel up to it today")]
204 ProgramUnwilling,
205 }
206
207 impl tor_error::HasKind for ExampleError {
208 fn kind(&self) -> tor_error::ErrorKind {
209 match self {
210 Self::SomethingExploded { .. } => tor_error::ErrorKind::Other,
211 Self::SomethingWasHidden(_, _) => tor_error::ErrorKind::RemoteHostNotFound,
212 Self::SomethingWasMissing(_) => tor_error::ErrorKind::FeatureDisabled,
213 Self::ProgramUnwilling => tor_error::ErrorKind::Internal,
214 }
215 }
216 }
217
218 macro_rules! assert_json_eq {
220 ($a:expr, $b:expr) => {
221 let json_a: serde_json::Value = serde_json::from_str($a).unwrap();
222 let json_b: serde_json::Value = serde_json::from_str($b).unwrap();
223 assert_eq!(json_a, json_b);
224 };
225 }
226
227 #[test]
228 fn serialize_error() {
229 let err = ExampleError::SomethingExploded {
230 what: "previous implementation".into(),
231 why: "worse things happen at C".into(),
232 };
233 let err = RpcError::from(err);
234 assert_eq!(err.code, RpcErrorKind::RequestError);
235 let serialized = serde_json::to_string(&err).unwrap();
236 let expected_json = r#"
237 {
238 "message": "error: The previous implementation exploded because worse things happen at C",
239 "code": 2,
240 "kinds": ["arti:Other"]
241 }
242 "#;
243 assert_json_eq!(&serialized, expected_json);
244
245 let err = ExampleError::SomethingWasHidden(
246 "zircon-encrusted tweezers".into(),
247 "chrome dinette".into(),
248 );
249 let err = RpcError::from(err);
250 let serialized = serde_json::to_string(&err).unwrap();
251 let expected = r#"
252 {
253 "message": "error: I'm hiding the zircon-encrusted tweezers in my chrome dinette",
254 "code": 2,
255 "kinds": ["arti:RemoteHostNotFound"]
256 }
257 "#;
258 assert_json_eq!(&serialized, expected);
259
260 let err = ExampleError::SomethingWasMissing("turbo-encabulator".into());
261 let err = RpcError::from(err);
262 let serialized = serde_json::to_string(&err).unwrap();
263 let expected = r#"
264 {
265 "message": "error: The turbo-encabulator was missing",
266 "code": 2,
267 "kinds": ["arti:FeatureDisabled"]
268 }
269 "#;
270 assert_json_eq!(&serialized, expected);
271
272 let err = ExampleError::ProgramUnwilling;
273 let err = RpcError::from(err);
274 let serialized = serde_json::to_string(&err).unwrap();
275 let expected = r#"
276 {
277 "message": "error: I don't feel up to it today",
278 "code": -32603,
279 "kinds": ["arti:Internal"]
280 }
281 "#;
282 assert_json_eq!(&serialized, expected);
283 }
284
285 #[test]
286 fn create_error() {
287 let mut e = RpcError::new("Example error".to_string(), RpcErrorKind::RequestError);
288 e.set_kind(tor_error::ErrorKind::CacheCorrupted);
289 e.set_datum("rpc:example".to_string(), "Hello world".to_string())
290 .unwrap();
291 let serialized = serde_json::to_string(&e).unwrap();
292 let expected = r#"
293 {
294 "message": "Example error",
295 "code": 2,
296 "kinds": ["arti:CacheCorrupted"],
297 "data": {
298 "rpc:example": "Hello world"
299 }
300 }
301 "#;
302 assert_json_eq!(&serialized, expected);
303 }
304}