use proc_macro2::TokenStream; pub fn attribute_native_enum( native_type: TokenStream, structure: synstructure::Structure, ) -> TokenStream { let ast = structure.ast(); let in_attrs = ast.attrs.iter().map(|a| quote!{#a}).collect::(); let in_vis = &ast.vis; let name = &ast.ident; let known_name = syn::Ident::new(&format!("{}Known", name), proc_macro2::Span::call_site()); let enumdata = match &ast.data { syn::Data::Enum(de) => de, _ => panic!("not an enum"), }; let known_enum; { let variants = &enumdata.variants; let doc_str = format!("Known enum variants of [`{}`]\n\n[`{}`]: struct.{}.html\n", name, name, name); known_enum = quote! { #[doc = #doc_str] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[repr(#native_type)] #[allow(non_camel_case_types)] #in_vis enum #known_name { #variants } } } let mut consts = TokenStream::new(); let mut convert = TokenStream::new(); for variant in &enumdata.variants { let variant_attrs = variant.attrs.iter().map(|a| quote!{#a}).collect::(); let disc_name = &variant.ident; let disc = variant.discriminant.as_ref().map(|(_, d)| quote!{#d}).unwrap_or_else(|| quote!{ #known_name::#disc_name as #native_type }); consts.extend(quote! { #variant_attrs pub const #disc_name: Self = #name(#disc); }); convert.extend(quote! { Self::#disc_name => #known_name::#disc_name, }); } quote! { #known_enum #in_attrs #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #in_vis struct #name(pub #native_type); impl #name { #consts /// Try converting to known enum values pub fn into_known(self) -> Option<#known_name> { Some( #[allow(unreachable_patterns)] match self { #convert _ => return None, } ) } } impl core::fmt::Debug for #name { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self.into_known() { Some(v) => v.fmt(f), None => f.debug_tuple(stringify!(#name)).field(&self.0).finish(), } } } impl From<#known_name> for #name { fn from(v: #known_name) -> Self { #name(v as #native_type) } } } }