Skip to main content

pleme_kindstr_derive/
lib.rs

1//!GENERATED by tatara-rust-derive::KindRoundTripSpec.
2//!Do not hand-edit; regenerate from the (defkind-round-trip …) source.
3use 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}