aws_smithy_types/
type_erasure.rs1use 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}