devela/code/util/
impl_trait.rs

1// devela::code::util::impl_trait
2
3/// A helper macro to concisely implement a few common utility traits.
4///
5/// ## Traits supported
6/// - Hash
7/// - fmt:: Debug, Display…
8///
9/// ## Features:
10/// - Allows multiple types in a single invocation, separated by semicolon
11/// - Supports types with or without lifetimes and generics.
12/// - Comma between lifetimes and generics is optional but recommended for clarity.
13/// - Requires the same formatting trait (`fmt::<trait>`) to be implemented for all generics.
14///
15/// ## Example
16/// ```
17/// # use devela::impl_trait;
18/// struct S0(usize);
19/// struct S1<T> { v: T }
20/// struct S2<'a, T> { v: &'a T }
21///
22/// impl_trait![Hash for S0 |self, state| self.0.hash(state)];
23///
24/// impl_trait![fmt::Binary for S1<T> where T |self, f| self.v.fmt(f)];
25///
26/// impl_trait!{fmt::Debug for S1<T> where T; S2<'a, T> where T |self, f| {
27///     write!(f, "S? {{ v: {:?} }}", self.v)
28/// }}
29/// ```
30// IMPROVE: support const-generic arguments, like e.g. for NonValue*
31// IMPROVE? support `$generic` as `ty` instead of `ident` (e.g. for `Divisor`)
32#[macro_export]
33#[cfg_attr(cargo_primary_package, doc(hidden))]
34macro_rules! impl_trait {
35    (Hash for
36     $(
37        $type:ident<$($lt:lifetime),* $(,)? $($generic:ident),*>
38        $(where $($bounded:ident),+ )?
39     );+ |$self:ident, $state:ident| $expr:expr) => {
40        $(
41            impl<$($lt,)* $($generic,)*> $crate::Hash for $type<$($lt,)* $($generic,)*>
42                $(where $($bounded: $crate::Hash,)+ )? {
43                fn hash<__H: $crate::Hasher>(&$self, $state: &mut __H) { $expr }
44            }
45        )+
46    };
47    (Hash for $($type:ident),+ |$self:ident, $state:ident| $expr:expr) => {
48        $(
49            impl $crate::Hash for $type {
50                fn hash<__H: $crate::Hasher>(&$self, $state: &mut __H) { $expr }
51            }
52        )+
53    };
54
55    (fmt::$trait:ident for
56     $(
57        $type:ident<$($lt:lifetime),* $(,)? $($generic:ident),*>
58        $(where $($bounded:ident),+ )?
59     );+ |$self:ident, $f:ident| $expr:expr) => {
60        $(
61            impl<$($lt,)* $($generic,)*> $crate::$trait for $type<$($lt,)* $($generic,)*>
62                $(where $($bounded: $crate::$trait,)+ )? {
63                fn fmt(&$self, $f: &mut $crate::Formatter<'_>) -> $crate::FmtResult<()> { $expr }
64            }
65        )+
66    };
67    (fmt::$trait:ident for $($type:ident),+ |$self:ident, $f:ident| $expr:expr) => {
68        $(
69            impl $crate::$trait for $type {
70                fn fmt(&$self, $f: &mut $crate::Formatter<'_>) -> $crate::FmtResult<()> { $expr }
71            }
72        )+
73    };
74}
75
76#[doc(inline)]
77pub use impl_trait;
78
79#[cfg(test)]
80#[rustfmt::skip]
81mod tests {
82    use crate::{format_buf, PhantomData};
83
84    struct S1 { v: i32 }
85    struct S2 { v: i32 }
86    struct GS1<T, U> { v1: T, v2: U }
87    struct GS2<'a, T, U, V> { v1: T, v2: U, _v3: PhantomData<&'a V> }
88
89    // multiple types without generics
90    impl_trait!(fmt::Debug for S1, S2 |self, f| {
91        write!(f, "S? {{ v: {:?} }}", self.v)
92    });
93    // multiple types with different generics
94    impl_trait!(fmt::Debug for GS1<T, U> where T, U; GS2<'a, T, U, V> where T, U |self, f| {
95        // all sharing the same implementation
96        write!(f, "GS? {{ v1: {:?}, v2: {:?} }}", self.v1, self.v2)
97    });
98
99    #[test]
100    fn impl_non_generic_debug() {
101        let s1 = S1 { v: 42 };
102        let s2 = S2 { v: 84 };
103        let mut buf = [0; 64];
104        assert_eq!(format_buf!(&mut buf, "{:?}", s1).unwrap(), "S? { v: 42 }");
105        assert_eq!(format_buf!(&mut buf, "{:?}", s2).unwrap(), "S? { v: 84 }");
106    }
107
108    #[test]
109    fn impl_generic_debug() {
110        let g1 = GS1 { v1: "hello", v2: "world" };
111        let g2 = GS2 { v1: 3.14, v2: 159, _v3: PhantomData::<&'_ i32> };
112        let mut buf = [0; 64];
113        assert_eq!(format_buf!(&mut buf, "{:?}", g1).unwrap(), "GS? { v1: \"hello\", v2: \"world\" }");
114        assert_eq!(format_buf!(&mut buf, "{:?}", g2).unwrap(), "GS? { v1: 3.14, v2: 159 }");
115    }
116}