easy_conv/lib.rs
1//! Cut down on trivial `impl From<A> for B` boilerplate code.
2
3#[cfg(test)]
4mod test;
5
6/// Implement conversion from `T` to its newtype wrapper.
7///
8/// Also works on types that convert to `T` via `into()`.
9///
10/// The wrapper can either be a newtype struct or a newtype variant of an enum:
11/// - Struct
12/// - Given `struct Foo(T);` and `impl From<U> for T`
13/// - `newtype_wrap!(Foo, T);` implements `T -> Foo(T)`
14/// - `newtype_wrap!(Foo, U);` implements `U -> Foo(T)`
15/// - Enum
16/// - Given `enum Foo{ Bar(T) };` and `impl From<U> for T`
17/// - `newtype_wrap!(Foo, Bar, T);` implements `T -> Foo::Bar(T)`
18/// - `newtype_wrap!(Foo, Bar, U);` implements `U -> Foo::Bar(T)`
19#[macro_export]
20macro_rules! newtype_wrap {
21 ($wrapper: ident, $wrapped: ty) => {
22 impl From<$wrapped> for $wrapper {
23 fn from(val: $wrapped) -> Self {
24 $wrapper(val.into())
25 }
26 }
27 };
28 ($wrapper: ty, $variant: ident, $wrapped: ty) => {
29 impl From<$wrapped> for $wrapper {
30 fn from(val: $wrapped) -> Self {
31 <$wrapper>::$variant(val.into())
32 }
33 }
34 };
35}
36
37/// The same as [`newtype_wrap`], except that the wrapping is implemented on
38/// all types `U` that convert to `T` via `into()`.
39///
40/// Be careful when using this: there may be `U` types that you wish do not receive automatic wrapping.
41/// In such a case, you should use [`newtype_wrap`] on each `U` type separately.
42#[macro_export]
43macro_rules! newtype_wrap_from_any {
44 ($wrapper: ident, $wrapped: ty) => {
45 impl<T> From<T> for $wrapper
46 where
47 T: Into<$wrapped>,
48 {
49 fn from(val: T) -> Self {
50 $wrapper(val.into())
51 }
52 }
53 };
54 ($wrapper: ty, $variant: ident, $wrapped: ty) => {
55 impl<T> From<T> for $wrapper
56 where
57 T: Into<$wrapped>,
58 {
59 fn from(val: T) -> Self {
60 <$wrapper>::$variant(val.into())
61 }
62 }
63 };
64}
65
66/// Implement conversion from `A` to `Z` via `B`, `C`, ...
67///
68/// `B` needs to implement `From<A>`, `C` needs to implement `From<B>`, and so on.
69///
70/// Example: `chained_into!(A, B, C, D, E);` implements `From<A> for E` by
71/// converting `A` to `B`, then `B` to `C`, then `C` to `D`, and finally `D` to `E`.
72#[macro_export]
73macro_rules! chained_into {
74 ($source: ty, $($tail: ty),+) => {
75 chained_into!(@impl $source, chained_into!(@get_target $($tail),+), $($tail),+);
76 };
77 (@impl $source: ty, $target: ty, $($intermediate: ty),*) => {
78 impl From<$source> for $target {
79 fn from(val: $source) -> $target {
80 chained_into!(@next val, $($intermediate),*)
81 }
82 }
83 };
84 (@get_target $target: ty) => {
85 $target
86 };
87 (@get_target $_: ty, $($tail: ty),+) => {
88 chained_into!(@get_target $($tail),+)
89 };
90 (@next $val: expr, $intermediate: ty) => {
91 <$intermediate>::from($val)
92 };
93 (@next $val: expr, $intermediate: ty, $($tail: ty),+) => {
94 chained_into!(@next <$intermediate>::from($val), $($tail),+)
95 };
96}