convertable_errors/
lib.rs

1/// Derives From<T> implementations for enums whose variants can be expressed by such foreign
2/// types.
3///
4/// For example, this:
5/// ```
6/// mod serde_json {
7///     pub struct Error;
8/// }
9///
10/// enum MyError {
11///     SerializationError(serde_json::Error),
12/// }
13///
14/// impl From<serde_json::Error> for MyError {
15///     fn from(e: serde_json::Error) -> Self {
16///         Self::SerializationError(e)
17///     }
18/// }
19/// ```
20/// is condensed using convertable-errors into this:
21/// ```
22/// # use convertable_errors::convertable_error;
23///
24/// mod serde_json {
25///     # #[derive(PartialEq, Debug, Clone, Copy)]
26///     pub struct Error;
27/// }
28///
29/// convertable_error! {
30///     # #[derive(PartialEq, Debug)]
31///     enum MyError {
32///         (SerializationError(serde_json::Error), [(serde_json::Error, Self::SerializationError)])
33///     }
34/// }
35/// #
36/// # let err = serde_json::Error;
37/// # let my_err: MyError = err.into();
38/// # assert_eq!(my_err, MyError::SerializationError(err));
39/// ```
40/// 
41/// The syntax for defining a convertable enum with convertable-errors is as follows:
42/// - Each variant of an enum must be wrapped in a tuple: `enum MyError { (Variant(ForeignType)), (Variant1) }`
43/// - The first member of the tuple represents your variant. At the moment, only tuple variants and
44/// unit variants are supported bc I'm a lazy fuck.
45/// - The second member of the tuple (optional) represents the types that can be converted into
46/// that variant: `enum MyError { (Variant(ForeignType), [ ... ]), (Variant1) }`
47/// - The members of the convertable types array are each tuples representing the foreign type that
48/// can be converted into your enum and the closure or variant to apply the foreign value to:
49/// `[(ForeignType, Self::Variant)]`. Internally, this second member can be a closure `|x|
50/// Self::Variant(x)`, a unit variant closure `|_| Self::Variant1`, or simply a variant identifier
51/// where the value of the foreign type will be stored: `Self::Variant`. In practice, you can use
52/// this macro for any enum, but I find it most useful for Error-like enums.
53///
54/// NOTE: This isn't a serious project, I might have made some mistakes, so feel free to open a PR
55/// :) This is just a helpful snippet that I use and felt like sharing.
56#[macro_export]
57macro_rules! convertable_error {
58    // A variant can have foreign types it can be derived from. Each of these may have a converter
59    (
60        $(#[$supermeta:meta])*
61        $v:vis enum $name:ident {
62            $(
63                $(#[$meta:meta])*
64                ($variant:ident$(($($field:ty),*))?$(, $equivalents:tt)?)
65            ),+$(,)?
66        }
67    ) => {
68        $(#[$supermeta])*
69        $v enum $name {
70            $($(#[$meta])*$variant$(($($field),*))?),+
71        }
72
73        // Build From<Foreign> impls for each of the variants
74        $(convertable_error!(@from $name, ($variant$(, $equivalents)?));)+
75    };
76
77    (@from $name:ident, ($variant:ident, [$(($ftype:ty, $converter:expr)),+])) => {
78        $(impl From<$ftype> for $name {
79            fn from(v: $ftype) -> Self {
80                $converter(v)
81            }
82        })+
83    };
84    // function f(v: ForeignType) -> Self, or simply be type-to-type
85    (@from $name:ident, ($variant:ident, [$($ftype:ty),+])) => {
86        $(impl From<$ftype> for $name {
87            fn from(v: $ftype) -> Self {
88                Self::$variant(v)
89            }
90        })+
91    };
92    // A variant of an error without any equivalent foreign types needs no further computation.
93    (@from $name:ident, ($variant:ident)) => {};
94}