typestate_enum/
lib.rs

1#![doc = include_str!("../README.md")]
2/// Provides zero-sized types which implement a common trait.
3#[macro_export]
4macro_rules! typestate_enum {
5    (@elem
6        $vis:vis $name:ident {
7            $(#[$meta:meta])*
8            $elem:ident,
9            $(
10                $(#[$tail_meta:meta])*
11                $elems:ident
12            ),+
13        }
14    ) => {
15        typestate_enum!(@elem
16            $vis $name {
17                $(#[$meta])*
18                $elem
19            }
20        );
21        typestate_enum!(@elem
22            $vis $name {
23                $(
24                    $(#[$tail_meta])*
25                    $elems
26                ),+
27            }
28        );
29    };
30    (@elem
31        $vis:vis $name:ident {
32            $(#[$meta:meta])*
33            $elem:ident
34        }
35    ) => {
36        $(#[$meta])*
37        $vis struct $elem;
38        impl $name for $elem {}
39    };
40    (
41        $(#[$outer_meta:meta])*
42        $vis:vis $name:ident {
43            $(
44                $(#[$inner_meta:meta])*
45                $elems:ident
46            ),+
47            $(,)?
48        }
49    ) => {
50        $(#[$outer_meta])*
51        $vis trait $name {}
52
53        typestate_enum!(@elem
54            $vis $name {
55                $(
56                    $(#[$inner_meta])*
57                    $elems
58                ),+
59            }
60        );
61    };
62}
63
64#[cfg(test)]
65mod test {
66    use super::typestate_enum;
67    use std::marker::PhantomData;
68
69    typestate_enum! {
70        pub State {
71            Ready,
72            Working,
73            Complete
74        }
75    }
76
77    struct Action<S: State>(PhantomData<S>);
78
79    impl<S: State> Action<S> {
80        fn new() -> Self {
81            Action::<S>(PhantomData)
82        }
83    }
84
85    impl Action<Ready> {
86        fn start_work(self) -> Action<Working> {
87            Action::new()
88        }
89    }
90
91    impl Action<Working> {
92        fn complete_work(self) -> Action<Complete> {
93            Action::new()
94        }
95    }
96
97    impl Action<Complete> {}
98
99    #[test]
100    fn test_typestate_enum() {
101        let ready_action = Action::<Ready>::new();
102        let working_action = ready_action.start_work();
103        working_action.complete_work();
104    }
105}