rust-dnsbox/lib/dnsbox-derive/src/native_enum.rs

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