106 lines
2.2 KiB
Rust
106 lines
2.2 KiB
Rust
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
|
|
#[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)
|
|
}
|
|
}
|
|
}
|
|
}
|