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 #![allow(clippy::string_slice)] use super::*;
192
193 #[derive(Debug, thiserror::Error, serde::Serialize)]
194 enum ExampleError {
195 #[error("The {} exploded because {}", what, why)]
196 SomethingExploded { what: String, why: String },
197
198 #[error("I'm hiding the {0} in my {1}")]
199 SomethingWasHidden(String, String),
200
201 #[error("The {0} was missing")]
202 SomethingWasMissing(String),
203
204 #[error("I don't feel up to it today")]
205 ProgramUnwilling,
206 }
207
208 impl tor_error::HasKind for ExampleError {
209 fn kind(&self) -> tor_error::ErrorKind {
210 match self {
211 Self::SomethingExploded { .. } => tor_error::ErrorKind::Other,
212 Self::SomethingWasHidden(_, _) => tor_error::ErrorKind::RemoteHostNotFound,
213 Self::SomethingWasMissing(_) => tor_error::ErrorKind::FeatureDisabled,
214 Self::ProgramUnwilling => tor_error::ErrorKind::Internal,
215 }
216 }
217 }
218
219 macro_rules! assert_json_eq {
221 ($a:expr, $b:expr) => {
222 let json_a: serde_json::Value = serde_json::from_str($a).unwrap();
223 let json_b: serde_json::Value = serde_json::from_str($b).unwrap();
224 assert_eq!(json_a, json_b);
225 };
226 }
227
228 #[test]
229 fn serialize_error() {
230 let err = ExampleError::SomethingExploded {
231 what: "previous implementation".into(),
232 why: "worse things happen at C".into(),
233 };
234 let err = RpcError::from(err);
235 assert_eq!(err.code, RpcErrorKind::RequestError);
236 let serialized = serde_json::to_string(&err).unwrap();
237 let expected_json = r#"
238 {
239 "message": "error: The previous implementation exploded because worse things happen at C",
240 "code": 2,
241 "kinds": ["arti:Other"]
242 }
243 "#;
244 assert_json_eq!(&serialized, expected_json);
245
246 let err = ExampleError::SomethingWasHidden(
247 "zircon-encrusted tweezers".into(),
248 "chrome dinette".into(),
249 );
250 let err = RpcError::from(err);
251 let serialized = serde_json::to_string(&err).unwrap();
252 let expected = r#"
253 {
254 "message": "error: I'm hiding the zircon-encrusted tweezers in my chrome dinette",
255 "code": 2,
256 "kinds": ["arti:RemoteHostNotFound"]
257 }
258 "#;
259 assert_json_eq!(&serialized, expected);
260
261 let err = ExampleError::SomethingWasMissing("turbo-encabulator".into());
262 let err = RpcError::from(err);
263 let serialized = serde_json::to_string(&err).unwrap();
264 let expected = r#"
265 {
266 "message": "error: The turbo-encabulator was missing",
267 "code": 2,
268 "kinds": ["arti:FeatureDisabled"]
269 }
270 "#;
271 assert_json_eq!(&serialized, expected);
272
273 let err = ExampleError::ProgramUnwilling;
274 let err = RpcError::from(err);
275 let serialized = serde_json::to_string(&err).unwrap();
276 let expected = r#"
277 {
278 "message": "error: I don't feel up to it today",
279 "code": -32603,
280 "kinds": ["arti:Internal"]
281 }
282 "#;
283 assert_json_eq!(&serialized, expected);
284 }
285
286 #[test]
287 fn create_error() {
288 let mut e = RpcError::new("Example error".to_string(), RpcErrorKind::RequestError);
289 e.set_kind(tor_error::ErrorKind::CacheCorrupted);
290 e.set_datum("rpc:example".to_string(), "Hello world".to_string())
291 .unwrap();
292 let serialized = serde_json::to_string(&e).unwrap();
293 let expected = r#"
294 {
295 "message": "Example error",
296 "code": 2,
297 "kinds": ["arti:CacheCorrupted"],
298 "data": {
299 "rpc:example": "Hello world"
300 }
301 }
302 "#;
303 assert_json_eq!(&serialized, expected);
304 }
305}