Skip to content

Commit

Permalink
feature: support EnumFrom macro
Browse files Browse the repository at this point in the history
  • Loading branch information
rcrwhyg committed Sep 10, 2024
1 parent bff5e9d commit d56bce3
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@ license = "MIT"
proc-macro = true

[dependencies]
proc-macro2 = "1.0.86"
quote = "1.0.37"
syn = { version = "2.0.77", features = ["extra-traits"] }

[dev-dependencies]
anyhow = "1.0.87"
tokio = { version = "1.40.0", features = ["rt", "rt-multi-thread", "macros"] }
28 changes: 28 additions & 0 deletions examples/enum_macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use template::EnumFrom;

#[allow(unused)]
#[derive(Debug, EnumFrom)]
enum Direction {
Up(DirectionUp),
Down,
Left(u32),
Right(u32, u32),
}

#[allow(unused)]
#[derive(Debug)]
struct DirectionUp {
speed: u32,
}

impl DirectionUp {
fn new(speed: u32) -> Self {
Self { speed }
}
}

fn main() {
let up: Direction = DirectionUp::new(42).into();
let left: Direction = 10.into();
println!("Up: {:?}, Left: {:?}", up, left);
}
50 changes: 49 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,49 @@
// empty
// proc macro crate

use proc_macro::TokenStream;
use quote::quote;

// for enum, we'd like to generate From impls for each variant
#[proc_macro_derive(EnumFrom)]
pub fn derive_enum_from(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
// println!("{:#?}", input);

// get the ident
let ident = input.ident;
//get enum variants
let variants = match input.data {
syn::Data::Enum(data) => data.variants,
_ => panic!("EnumFrom can only be applied to enums"),
};
//for each variant, get the ident and fields
let from_impls = variants.iter().map(|v| {
let var = &v.ident;
match &v.fields {
syn::Fields::Unnamed(fields) => {
// only support one field
if fields.unnamed.len() != 1 {
quote! {}
} else {
let field = fields.unnamed.first().expect("Should have 1 field");
let ty = &field.ty;
quote! {
impl From<#ty> for #ident {
fn from(v: #ty) -> Self {
#ident::#var(v)
}
}
}
}
}
syn::Fields::Unit => quote! {},
syn::Fields::Named(_fields) => quote! {},
}
});

// quote return proc-macro2 TokenStream so we need to convert it to TokenStream
quote! {
#(#from_impls)*
}
.into()
}

0 comments on commit d56bce3

Please sign in to comment.