aws_smithy_types/
type_erasure.rs
1use std::any::Any;
7use std::error::Error as StdError;
8use std::fmt;
9use std::sync::Arc;
10
11pub struct TypeErasedBox {
39 field: Box<dyn Any + Send + Sync>,
40 #[allow(clippy::type_complexity)]
41 debug: Arc<
42 dyn Fn(&Box<dyn Any + Send + Sync>, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
43 >,
44 #[allow(clippy::type_complexity)]
45 clone: Option<Arc<dyn Fn(&Box<dyn Any + Send + Sync>) -> TypeErasedBox + Send + Sync>>,
46}
47
48#[cfg(feature = "test-util")]
49impl TypeErasedBox {
50 pub fn doesnt_matter() -> Self {
55 Self::new("doesn't matter")
56 }
57}
58
59impl fmt::Debug for TypeErasedBox {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 f.write_str("TypeErasedBox[")?;
62 if self.clone.is_some() {
63 f.write_str("Clone")?;
64 } else {
65 f.write_str("!Clone")?;
66 }
67 f.write_str("]:")?;
68 (self.debug)(&self.field, f)
69 }
70}
71
72impl TypeErasedBox {
73 pub fn new<T: Send + Sync + fmt::Debug + 'static>(value: T) -> Self {
75 let debug = |value: &Box<dyn Any + Send + Sync>, f: &mut fmt::Formatter<'_>| {
76 fmt::Debug::fmt(value.downcast_ref::<T>().expect("type-checked"), f)
77 };
78 Self {
79 field: Box::new(value),
80 debug: Arc::new(debug),
81 clone: None,
82 }
83 }
84
85 pub fn new_with_clone<T: Send + Sync + Clone + fmt::Debug + 'static>(value: T) -> Self {
87 let debug = |value: &Box<dyn Any + Send + Sync>, f: &mut fmt::Formatter<'_>| {
88 fmt::Debug::fmt(value.downcast_ref::<T>().expect("type-checked"), f)
89 };
90 let clone = |value: &Box<dyn Any + Send + Sync>| {
91 TypeErasedBox::new_with_clone(value.downcast_ref::<T>().expect("typechecked").clone())
92 };
93 Self {
94 field: Box::new(value),
95 debug: Arc::new(debug),
96 clone: Some(Arc::new(clone)),
97 }
98 }
99
100 pub fn try_clone(&self) -> Option<Self> {
104 Some((self.clone.as_ref()?)(&self.field))
105 }
106
107 pub fn downcast<T: fmt::Debug + Send + Sync + 'static>(self) -> Result<Box<T>, Self> {
109 let TypeErasedBox {
110 field,
111 debug,
112 clone,
113 } = self;
114 field.downcast().map_err(|field| Self {
115 field,
116 debug,
117 clone,
118 })
119 }
120
121 pub fn downcast_ref<T: fmt::Debug + Send + Sync + 'static>(&self) -> Option<&T> {
123 self.field.downcast_ref()
124 }
125
126 pub fn downcast_mut<T: fmt::Debug + Send + Sync + 'static>(&mut self) -> Option<&mut T> {
128 self.field.downcast_mut()
129 }
130}
131
132impl From<TypeErasedError> for TypeErasedBox {
133 fn from(value: TypeErasedError) -> Self {
134 TypeErasedBox {
135 field: value.field,
136 debug: value.debug,
137 clone: None,
138 }
139 }
140}
141
142pub struct TypeErasedError {
144 field: Box<dyn Any + Send + Sync>,
145 #[allow(clippy::type_complexity)]
146 debug: Arc<
147 dyn Fn(&Box<dyn Any + Send + Sync>, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
148 >,
149 #[allow(clippy::type_complexity)]
150 as_error: Box<dyn for<'a> Fn(&'a TypeErasedError) -> &'a (dyn StdError) + Send + Sync>,
151}
152
153impl fmt::Debug for TypeErasedError {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 f.write_str("TypeErasedError:")?;
156 (self.debug)(&self.field, f)
157 }
158}
159
160impl fmt::Display for TypeErasedError {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 fmt::Display::fmt((self.as_error)(self), f)
163 }
164}
165
166impl StdError for TypeErasedError {
167 fn source(&self) -> Option<&(dyn StdError + 'static)> {
168 (self.as_error)(self).source()
169 }
170}
171
172impl TypeErasedError {
173 pub fn new<T: StdError + Send + Sync + fmt::Debug + 'static>(value: T) -> Self {
175 let debug = |value: &Box<dyn Any + Send + Sync>, f: &mut fmt::Formatter<'_>| {
176 fmt::Debug::fmt(value.downcast_ref::<T>().expect("typechecked"), f)
177 };
178 Self {
179 field: Box::new(value),
180 debug: Arc::new(debug),
181 as_error: Box::new(|value: &TypeErasedError| {
182 value.downcast_ref::<T>().expect("typechecked") as _
183 }),
184 }
185 }
186
187 pub fn downcast<T: StdError + fmt::Debug + Send + Sync + 'static>(
189 self,
190 ) -> Result<Box<T>, Self> {
191 let TypeErasedError {
192 field,
193 debug,
194 as_error,
195 } = self;
196 field.downcast().map_err(|field| Self {
197 field,
198 debug,
199 as_error,
200 })
201 }
202
203 pub fn downcast_ref<T: StdError + fmt::Debug + Send + Sync + 'static>(&self) -> Option<&T> {
205 self.field.downcast_ref()
206 }
207
208 pub fn downcast_mut<T: StdError + fmt::Debug + Send + Sync + 'static>(
210 &mut self,
211 ) -> Option<&mut T> {
212 self.field.downcast_mut()
213 }
214
215 #[cfg(feature = "test-util")]
217 pub fn doesnt_matter() -> Self {
218 #[derive(Debug)]
219 struct FakeError;
220 impl fmt::Display for FakeError {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 write!(f, "FakeError")
223 }
224 }
225 impl StdError for FakeError {}
226 Self::new(FakeError)
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::{TypeErasedBox, TypeErasedError};
233 use std::fmt;
234
235 #[derive(Debug, Clone, PartialEq, Eq)]
236 struct TestErr {
237 inner: &'static str,
238 }
239
240 impl TestErr {
241 fn new(inner: &'static str) -> Self {
242 Self { inner }
243 }
244 }
245
246 impl fmt::Display for TestErr {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 write!(f, "Error: {}", self.inner)
249 }
250 }
251
252 impl std::error::Error for TestErr {}
253
254 #[test]
255 fn test_typed_erased_errors_can_be_downcast() {
256 let test_err = TestErr::new("something failed!");
257 let type_erased_test_err = TypeErasedError::new(test_err.clone());
258 let actual = type_erased_test_err
259 .downcast::<TestErr>()
260 .expect("type erased error can be downcast into original type");
261 assert_eq!(test_err, *actual);
262 }
263
264 #[test]
265 fn test_typed_erased_errors_can_be_unwrapped() {
266 let test_err = TestErr::new("something failed!");
267 let type_erased_test_err = TypeErasedError::new(test_err.clone());
268 let actual = *type_erased_test_err
269 .downcast::<TestErr>()
270 .expect("type erased error can be downcast into original type");
271 assert_eq!(test_err, actual);
272 }
273
274 #[test]
275 fn test_typed_cloneable_boxes() {
276 let expected_str = "I can be cloned";
277 let cloneable = TypeErasedBox::new_with_clone(expected_str.to_owned());
278 let cloned = cloneable.try_clone().unwrap();
280 let actual_str = cloned.downcast_ref::<String>().unwrap();
281 assert_eq!(expected_str, actual_str);
282 assert_ne!(format!("{expected_str:p}"), format! {"{actual_str:p}"});
284 }
285}