rpc_router/handler/
handler_error.rs

1use serde::{Serialize, Serializer};
2use serde_json::Value;
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5
6pub type HandlerResult<T> = core::result::Result<T, HandlerError>;
7
8type AnyMap = HashMap<TypeId, Box<dyn Any + Send + Sync>>;
9
10#[derive(Debug)]
11pub struct HandlerError {
12	holder: AnyMap,
13	type_name: &'static str,
14}
15
16impl HandlerError {
17	pub fn new<T>(val: T) -> HandlerError
18	where
19		T: Any + Send + Sync,
20	{
21		let mut holder = AnyMap::with_capacity(1);
22		let type_name = std::any::type_name::<T>();
23		holder.insert(TypeId::of::<T>(), Box::new(val));
24		HandlerError { holder, type_name }
25	}
26}
27
28impl HandlerError {
29	/// Returns an option containing a reference if the error contained within this error
30	/// matches the requested type.
31	pub fn get<T: Any + Send + Sync>(&self) -> Option<&T> {
32		self.holder
33			.get(&TypeId::of::<T>())
34			.and_then(|boxed_any| boxed_any.downcast_ref::<T>())
35	}
36
37	/// Same as `get::<T>()` but remove the date so that it returns a owned value.
38	pub fn remove<T: Any + Send + Sync>(&mut self) -> Option<T> {
39		self.holder.remove(&TypeId::of::<T>()).and_then(|boxed_any| {
40			// Attempt to downcast the Box<dyn Any> into Box<T>. If successful, take the value out of the box.
41			(boxed_any as Box<dyn Any>).downcast::<T>().ok().map(|boxed| *boxed)
42		})
43	}
44
45	/// Return the type name of the error hold by this RpcHandlerError
46	pub fn type_name(&self) -> &'static str {
47		self.type_name
48	}
49}
50
51// Implementing Serialize for RpcHandlerError
52impl Serialize for HandlerError {
53	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
54	where
55		S: Serializer,
56	{
57		// By default, serialization will only serialize an informative message regarding the type of error contained,
58		// as we do not have more information at this point.
59		// NOTE: It is currently uncertain whether we should require serialization for the RpcHandlerError contained type.
60		serializer.serialize_str(&format!("RpcHandlerError containing error '{}'", self.type_name))
61	}
62}
63
64// region:    --- IntoRpcHandlerError
65
66/// A trait with a default implementation that converts any application error
67/// into a `RpcHandlerError`. This allows the application code
68/// to query and extract the specified application error.
69pub trait IntoHandlerError
70where
71	Self: Sized + Send + Sync + 'static,
72{
73	fn into_handler_error(self) -> HandlerError {
74		HandlerError::new(self)
75	}
76}
77
78impl IntoHandlerError for HandlerError {
79	fn into_handler_error(self) -> HandlerError {
80		self
81	}
82}
83
84impl IntoHandlerError for String {
85	fn into_handler_error(self) -> HandlerError {
86		HandlerError::new(self)
87	}
88}
89
90impl IntoHandlerError for &'static str {
91	fn into_handler_error(self) -> HandlerError {
92		HandlerError::new(self)
93	}
94}
95
96impl IntoHandlerError for Value {
97	fn into_handler_error(self) -> HandlerError {
98		HandlerError::new(self)
99	}
100}
101
102// endregion: --- IntoRpcHandlerError
103
104// region:    --- Error Boilerplate
105
106impl core::fmt::Display for HandlerError {
107	fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error> {
108		write!(fmt, "{self:?}")
109	}
110}
111
112impl std::error::Error for HandlerError {}
113
114// endregion: --- Error Boilerplate