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) } } } }