Skip to main content

anyerr/
converter.rs

1use std::fmt::Debug;
2
3use crate::context::AnyValue;
4
5/// The trait used to select the method to transforming values.
6///
7/// Implementers of this trait define how values are mapped to other types. For
8/// example, the [`DebugConverter`] implements a conversion using the
9/// [`Debug::fmt()`] method to format values as strings. This is a trait that
10/// doesn't actually perform the conversion itself, but specifies how values
11/// should be transformed.
12///
13/// For more converters, refer to the [`crate::converter`] module. To learn
14/// about the underlying type that works with the conversion, see the
15/// [`Convertable`] trait.
16pub trait Converter: Debug + Send + Sync + 'static {}
17
18/// A converter that uses the [`Debug`] trait to format values
19#[derive(Debug)]
20pub struct DebugConverter;
21
22impl Converter for DebugConverter {}
23
24/// A converter that uses the [`Into`] trait to convert values into another
25/// type.
26#[derive(Debug)]
27pub struct IntoConverter;
28
29impl Converter for IntoConverter {}
30
31/// A converter that converts values into a type-erased [`Box`] containing any
32/// value.
33#[derive(Debug)]
34pub struct BoxConverter;
35
36impl Converter for BoxConverter {}
37
38/// The trait marking a type that is able to be converted to another one using
39/// a [`Converter`].
40///
41/// The [`Convertable`] trait allows types to be converted into another type `T`
42/// using a specified converter `C`.
43pub trait Convertable<C: Converter, T>: Sized {
44    /// Converts the value into another type `T` using the specified converter
45    /// `C`. The conversion should not fail, so not a [`Result<T, E>`] but a
46    /// `T` is expected.
47    ///
48    /// # Examples
49    ///
50    /// ```rust
51    /// # use anyerr::converter::{Convertable, DebugConverter};
52    /// assert_eq!(<_ as Convertable<DebugConverter, String>>::to(42), "42");
53    /// assert_eq!(<_ as Convertable<DebugConverter, String>>::to("str"), "\"str\"");
54    /// ```
55    fn to(self) -> T;
56}
57
58impl<S: Debug, T: From<String>> Convertable<DebugConverter, T> for S {
59    fn to(self) -> T {
60        format!("{self:?}").into()
61    }
62}
63
64impl<S: Into<T>, T> Convertable<IntoConverter, T> for S {
65    fn to(self) -> T {
66        self.into()
67    }
68}
69
70impl<S, T> Convertable<BoxConverter, T> for S
71where
72    S: AnyValue,
73    T: From<Box<dyn AnyValue + Send + Sync + 'static>>,
74{
75    fn to(self) -> T {
76        let res: Box<dyn AnyValue + Send + Sync + 'static> = Box::new(self);
77        res.into()
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    type BoxAnyValue = Box<dyn AnyValue + Send + Sync + 'static>;
86
87    #[test]
88    fn converter_convert_succeeds() {
89        assert_eq!(<_ as Convertable<DebugConverter, String>>::to(1), "1");
90        assert_eq!(
91            <_ as Convertable<DebugConverter, String>>::to("1"),
92            r#""1""#
93        );
94
95        assert_eq!(
96            <_ as Convertable<IntoConverter, String>>::to("str"),
97            String::from("str")
98        );
99
100        let res = <_ as Convertable<BoxConverter, BoxAnyValue>>::to("1");
101        assert_eq!(format!("{res:?}"), "\"1\"");
102    }
103
104    #[test]
105    fn converter_convert_succeeds_when_delegated_by_function() {
106        fn do_with<C: Converter, S: Convertable<C, T>, T>(source: S) -> T {
107            source.to()
108        }
109
110        assert_eq!(do_with::<DebugConverter, _, String>(1), "1");
111        assert_eq!(do_with::<DebugConverter, _, String>("1"), "\"1\"");
112
113        assert_eq!(
114            do_with::<IntoConverter, _, String>("str"),
115            String::from("str")
116        );
117
118        let res = do_with::<BoxConverter, _, BoxAnyValue>("1");
119        assert_eq!(format!("{res:?}"), "\"1\"");
120    }
121}