From 4c0f94bd48fb391ea5c0d2d9fe93c171d5fd07f7 Mon Sep 17 00:00:00 2001 From: Jessi Date: Mon, 10 Jun 2024 14:28:36 -0400 Subject: [PATCH] from_repr --- proc_macro/src/from_repr.rs | 64 +++++++++++++++++++++++++++++++++++++ proc_macro/src/lib.rs | 6 ++++ src/lib.rs | 13 +++++--- 3 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 proc_macro/src/from_repr.rs diff --git a/proc_macro/src/from_repr.rs b/proc_macro/src/from_repr.rs new file mode 100644 index 0000000..a3b7a79 --- /dev/null +++ b/proc_macro/src/from_repr.rs @@ -0,0 +1,64 @@ +use proc_macro::{TokenStream}; + +const VALID: &[&'static str] = &[ + "u8", "u16", "u32", "u64", "u128", + "i8", "i16", "i32", "i64", "i128", +]; + +pub fn derive_from_repr(input: TokenStream) -> TokenStream { + let syn::DeriveInput { attrs, ident, data, .. } = syn::parse_macro_input!(input); + let edata = match data { + syn::Data::Enum(edata) => edata, + _invalid => return TokenStream::from(quote::quote! { compile_error!("The identifier should not be empty"); }), + }; + + let repr_type = attrs.iter() + .filter_map(|attribute| { + // verify it is a repr type + attribute.path().get_ident()? + .to_string().eq("repr") + .then_some(())?; + let mut repr_type: Option = None; + let _ = attribute.parse_nested_meta(|meta| { + for &v in VALID { + if meta.path.is_ident(v) { + repr_type = Some(meta.path); + return Ok(()) + } + } + Err(meta.error("unrecognized repr")) + }); + repr_type + }).next(); + + let repr = match repr_type { + Some(repr) => repr, + None => { + return TokenStream::from(quote::quote! { + compile_error!("PrimitiveEnum requires the #[repr(u/i*)] attribute"); + }) + } + }; + + let values = edata.variants.iter().filter_map(|variant| { + if let Some((_, desc)) = &variant.discriminant { + let variant = &variant.ident; + Some(quote::quote! { + #desc => Some(Self::#variant), + }) + } else { None } + }); + + let name = &ident; + TokenStream::from(quote::quote! { + impl eset::FromRepr for #name { + type Repr = #repr; + fn from_repr(repr: #repr) -> Option { + match repr { + #(#values)* + _ => None + } + } + } + }) +} \ No newline at end of file diff --git a/proc_macro/src/lib.rs b/proc_macro/src/lib.rs index 62ac1d9..cd62d47 100644 --- a/proc_macro/src/lib.rs +++ b/proc_macro/src/lib.rs @@ -5,8 +5,14 @@ use proc_macro::*; mod sigscan; +mod from_repr; #[proc_macro] pub fn signature(input: TokenStream) -> TokenStream { sigscan::macro_proc(input) +} + +#[proc_macro_derive(FromRepr)] +pub fn derive_from_repr(input: TokenStream) -> TokenStream { + from_repr::derive_from_repr(input) } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 2116162..390f8d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,9 +32,6 @@ pub mod win32; #[cfg(feature = "win32")] pub use win32::{ image_base, image_header, find_kernel32, process_executable }; -/// re-export the signature macro -pub use xgen::signature; - mod time; pub use time::dur; @@ -45,4 +42,12 @@ mod strings; pub use strings::*; mod iter_tools; -pub use iter_tools::*; \ No newline at end of file +pub use iter_tools::*; + +/// re-export the signature macro +pub use xgen::signature; +pub use xgen::FromRepr; +pub trait FromRepr: Sized { + type Repr; + fn from_repr(repr: Self::Repr) -> Option; +}