1use proc_macro::TokenStream;
2use proc_macro2::Literal;
3use quote::quote;
4use syn::{Ident, LitInt, Token, parse_macro_input};
5
6#[proc_macro]
7pub fn enum_segment(input: TokenStream) -> TokenStream {
8 let input = parse_macro_input!(input as EnumSegmentInput);
9
10 let name = input.name;
11 let repr_type = input.repr_type;
12 let start = input.start;
13 let end = input.end;
14 let prefix = input.prefix;
15 let width = input.width;
16
17 let mut variants = Vec::new();
18
19 for i in start..=end {
20 let variant_name = format!("{prefix}{:0width$}", i, width = width as usize);
21 let variant_ident = Ident::new(&variant_name, name.span());
22 let variant_value = Literal::i32_unsuffixed(i);
23
24 variants.push(quote! {
25 #[allow(non_camel_case_types)]
26 #variant_ident = #variant_value,
27 });
28 }
29
30 let expanded = quote! {
31 #[allow(non_camel_case_types)]
32 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
33 #[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
34 #[repr(#repr_type)]
35 #[non_exhaustive]
36 pub enum #name {
37 #(#variants)*
38 }
39 impl std::fmt::Display for #name {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 write!(f, "{:0>width$}", #repr_type::from(*self), width = #width as usize)
42 }
43 }
44 };
45
46 TokenStream::from(expanded)
47}
48
49struct EnumSegmentInput {
50 name: Ident,
51 repr_type: Ident,
52 start: i32,
53 end: i32,
54 prefix: String,
55 width: u32,
56}
57
58impl syn::parse::Parse for EnumSegmentInput {
59 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
60 let name: Ident = input.parse()?;
61 input.parse::<Token![,]>()?;
62
63 let repr_type: Ident = input.parse()?;
64 input.parse::<Token![,]>()?;
65
66 let start: LitInt = input.parse()?;
67 input.parse::<Token![,]>()?;
68
69 let end: LitInt = input.parse()?;
70 input.parse::<Token![,]>()?;
71
72 let prefix: Ident = input.parse()?;
73 input.parse::<Token![,]>()?;
74
75 let width: LitInt = input.parse()?;
76
77 Ok(EnumSegmentInput {
78 name,
79 repr_type,
80 start: start.base10_parse::<i32>()?,
81 end: end.base10_parse::<i32>()?,
82 prefix: prefix.to_string(),
83 width: width.base10_parse::<u32>()?,
84 })
85 }
86}