2019-07-04 21:54:43 +00:00
|
|
|
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::<TokenStream>();
|
|
|
|
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::<TokenStream>();
|
|
|
|
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
|
2020-03-07 15:10:59 +00:00
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
2019-07-04 21:54:43 +00:00
|
|
|
#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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|