pleme_kindstr_derive/
lib.rs1use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, LitStr};
6#[proc_macro_derive(KindStr, attributes(kind))]
7pub fn derive_kind_str(input: TokenStream) -> TokenStream {
8 let input = parse_macro_input!(input as DeriveInput);
9 let self_name = &input.ident;
10 let de = match &input.data {
11 Data::Enum(DataEnum { variants, .. }) => variants,
12 _ => {
13 return syn::Error::new_spanned(self_name, "KindStr requires an enum")
14 .to_compile_error()
15 .into();
16 }
17 };
18 let mut as_str_arms = Vec::new();
19 let mut from_str_arms = Vec::new();
20 let mut errors = Vec::new();
21 for v in de.iter() {
22 let variant_name = &v.ident;
23 if !matches!(v.fields, Fields::Unit) {
24 errors
25 .push(
26 syn::Error::new_spanned(
27 variant_name,
28 "KindStr requires unit (fieldless) variants",
29 )
30 .to_compile_error(),
31 );
32 continue;
33 }
34 let mut name = variant_name.to_string();
35 let mut aliases: Vec<String> = Vec::new();
36 for attr in &v.attrs {
37 if !attr.path().is_ident("kind") {
38 continue;
39 }
40 let r = attr
41 .parse_nested_meta(|meta| {
42 if meta.path.is_ident("name") {
43 let s: LitStr = meta.value()?.parse()?;
44 name = s.value();
45 } else if meta.path.is_ident("alias") {
46 let s: LitStr = meta.value()?.parse()?;
47 aliases.push(s.value());
48 } else {
49 return Err(
50 meta.error("unknown `kind` key (expected name/alias/byte)"),
51 );
52 }
53 Ok(())
54 });
55 if let Err(e) = r {
56 errors.push(e.to_compile_error());
57 }
58 }
59 as_str_arms
60 .push(
61 quote! {
62 Self::# variant_name => # name,
63 },
64 );
65 from_str_arms
66 .push(
67 quote! {
68 # name => ::core::option::Option::Some(Self::# variant_name),
69 },
70 );
71 for a in &aliases {
72 from_str_arms
73 .push(
74 quote! {
75 # a => ::core::option::Option::Some(Self::# variant_name),
76 },
77 );
78 }
79 }
80 if !errors.is_empty() {
81 let mut ts = proc_macro2::TokenStream::new();
82 for e in errors {
83 ts.extend(e);
84 }
85 return ts.into();
86 }
87 let expanded = quote! {
88 impl # self_name { #[doc =
89 "Wire string for this variant (paired inverse of `from_str_kind`)."] pub fn
90 as_str(& self) -> &'static str { match self { # (# as_str_arms) * } } #[doc =
91 "Parse a variant from its wire string (paired inverse of `as_str`)."] pub fn
92 from_str_kind(s : & str) -> ::core::option::Option < Self > { match s { # (#
93 from_str_arms) * _ => ::core::option::Option::None, } } }
94 };
95 TokenStream::from(expanded)
96}