stable_step_derive/
lib.rs

1use proc_macro::{self, TokenStream};
2use quote::quote;
3use syn::{parse_macro_input, Data, DeriveInput};
4
5#[proc_macro_derive(Step)]
6pub fn derive(input: TokenStream) -> TokenStream {
7    let DeriveInput {
8        ident,
9        generics,
10        data,
11        ..
12    } = parse_macro_input!(input);
13
14    let data = if let Data::Enum(data) = data {
15        data
16    } else {
17        panic!("Step can only be derived for Enums");
18    };
19
20    for variant in &data.variants {
21        if !variant.fields.is_empty() {
22            panic!("Step can only be derived for Enums with no variant fields");
23        }
24    }
25
26    if data.variants.is_empty() {
27        panic!("Step cannot be derived for an empty Enum");
28    }
29
30    let first = data.variants.iter().next().unwrap();
31    let last = data.variants.iter().last().unwrap();
32
33    let nexts: proc_macro2::TokenStream = data
34        .variants
35        .iter()
36        .zip(data.variants.iter().skip(1))
37        .map(|(lesser, greater)| {
38            let lesser = &lesser.ident;
39            let greater = &greater.ident;
40            quote! {
41                Self::#lesser => Some(Self::#greater),
42            }
43        })
44        .collect();
45
46    let prevs: proc_macro2::TokenStream = data
47        .variants
48        .iter()
49        .zip(data.variants.iter().skip(1))
50        .map(|(lesser, greater)| {
51            let lesser = &lesser.ident;
52            let greater = &greater.ident;
53            quote! {
54                Self::#greater => Some(Self::#lesser),
55            }
56        })
57        .collect();
58
59    let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
60
61    let output = quote! {
62        impl #impl_generics ::stable_step::Step for #ident #type_generics #where_clause {
63            const MIN: Self = Self::#first;
64            const MAX: Self = Self::#last;
65
66            fn next(&self) -> Option<Self>
67            where
68                Self: Sized,
69            {
70                match self {
71                    Self::#last => None,
72                    #nexts
73                }
74            }
75
76            fn prev(&self) -> Option<Self>
77            where
78                Self: Sized,
79            {
80                match self {
81                    Self::#first => None,
82                    #prevs
83                }
84            }
85        }
86    };
87
88    output.into()
89}