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}