Skip to content

Commit

Permalink
✨ Support single level enums' auto! macro first.
Browse files Browse the repository at this point in the history
  • Loading branch information
langyo committed Nov 13, 2024
1 parent cfb6e09 commit ab11d3c
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 26 deletions.
53 changes: 48 additions & 5 deletions yuuka-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,58 @@ pub fn auto(input: TokenStream) -> TokenStream {
})
.collect::<Vec<_>>();

let ret: proc_macro::TokenStream = quote! {
quote! {
#ident {
#( #list ),*
}
}
.into();
dbg!(ret.to_string());
ret
.into()
}

AutoMacrosType::EnumEmpty(key) => quote! {
#ident::#key
}
.into(),
AutoMacrosType::EnumStruct((key, items)) => {
let list = items
.iter()
.map(|(item_key, value)| {
quote! {
#item_key: #macro_ident!(#key #item_key #value)
}
})
.collect::<Vec<_>>();

quote! {
#ident::#key {
#( #list ),*
}
}
.into()
}
AutoMacrosType::EnumTuple((key, items)) => {
if items.len() == 1 {
let first_item = items.first().expect("Failed to get first item");
quote! {
#ident::#key(#macro_ident!(#key #first_item))
}
.into()
} else {
let list = items
.iter()
.enumerate()
.map(|(index, item)| {
quote! {
#macro_ident!(#key #index #item)
}
})
.collect::<Vec<_>>();

quote! {
#ident::#key(#( #list ),*)
}
.into()
}
}
_ => todo!("auto for enum"),
}
}
79 changes: 77 additions & 2 deletions yuuka-macros/src/template/auto_enums.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,82 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::Ident;

use crate::tools::EnumsFlatten;
use crate::tools::{EnumValueFlatten, EnumsFlatten};

pub(crate) fn generate_enums_auto_macros(enums: EnumsFlatten) -> Vec<TokenStream> {
vec![]
enums
.iter()
.map(|(ident, v, _default_value, _extra_macros)| {
let rules = v
.iter()
.map(|(name, ty, _)| match ty {
EnumValueFlatten::Empty => {
quote! {}
}
EnumValueFlatten::Struct(items) => {
let list = items
.iter()
.map(|(key, ty, _default_value, _extra_macros)| {
quote! {
(#name #key { $($val:tt)+ }) => {
::yuuka::auto!(#ty { $($val)+ })
};
}
})
.collect::<Vec<_>>();
quote! {
#(#list)*
}
}
EnumValueFlatten::Tuple(items) => {
if items.len() == 1 {
let ty = items.first().expect("Failed to get first item");
quote! {
(#name { $($val:tt)+ }) => {
::yuuka::auto!(#ty { $($val)+ })
};
}
} else {
let list = items
.iter()
.enumerate()
.map(|(i, ty)| {
let i = syn::Index::from(i);
quote! {
(#name #i { $($val:tt)+ }) => {
::yuuka::auto!(#ty { $($val)+ })
};
}
})
.collect::<Vec<_>>();
quote! {
#(#list)*
}
}
}
})
.collect::<Vec<_>>();
let rules = quote! {
#(#rules)*
};

let ident = Ident::new(format!("__auto_{}", ident).as_str(), ident.span());
quote! {
#[doc(hidden)]
macro_rules! #ident {
() => {};

#rules

($name:ident $key:ident $val:expr) => {
$val
};
($name:ident $val:expr) => {
$val
};
}
}
})
.collect::<Vec<_>>()
}
2 changes: 1 addition & 1 deletion yuuka-macros/src/template/auto_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub(crate) fn generate_structs_auto_macros(structs: StructsFlatten) -> Vec<Token
.iter()
.map(|(name, ty, _, _)| {
quote! {
(#name { $($val: tt)+ }) => {
(#name { $($val:tt)+ }) => {
::yuuka::auto!(#ty { $($val)+ })
};
}
Expand Down
103 changes: 98 additions & 5 deletions yuuka-macros/src/tools/auto_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use syn::{
#[derive(Debug, Clone)]
pub enum AutoMacrosType {
Struct(Vec<(Ident, TokenStream)>),
Enum,
EnumEmpty(Ident),
EnumStruct((Ident, Vec<(Ident, TokenStream)>)),
EnumTuple((Ident, Vec<TokenStream>)),
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -80,11 +82,102 @@ impl Parse for AutoMacros {
ident,
body: AutoMacrosType::Struct(tokens),
})
} else if input.peek(Token![::]) {
input.parse::<Token![::]>()?;
let key: Ident = input.parse()?;

if input.peek(token::Brace) {
// Sth::Sth { key: ..., ... }

let content;
braced!(content in input);

let mut items = vec![];
while !content.is_empty() {
let key: Ident = content.parse()?;
content.parse::<Token![:]>()?;

if content.peek(token::Brace) {
let inner_content;
braced!(inner_content in content);
let inner_content: TokenStream = inner_content.parse()?;
items.push((
key,
quote! {
{ #inner_content }
},
));
} else if content.peek(token::Bracket) {
let inner_content;
bracketed!(inner_content in content);
let inner_content: TokenStream = inner_content.parse()?;
items.push((
key,
quote! {
[ #inner_content ]
},
));
} else if content.peek(token::Paren) {
let inner_content;
parenthesized!(inner_content in content);
let inner_content: TokenStream = inner_content.parse()?;
items.push((
key,
quote! {
( #inner_content )
},
));
} else {
let value: Expr = content.parse()?;
items.push((
key,
quote! {
#value
},
));
}

if content.peek(Token![,]) {
content.parse::<Token![,]>()?;
}
}

Ok(AutoMacros {
ident,
body: AutoMacrosType::EnumStruct((key, items)),
})
} else if input.peek(token::Paren) {
// Sth::Sth(key, ...)

let content;
parenthesized!(content in input);

let mut items = vec![];
while !content.is_empty() {
let value: Expr = content.parse()?;
items.push(quote! {
#value
});

if content.peek(Token![,]) {
content.parse::<Token![,]>()?;
}
}

Ok(AutoMacros {
ident,
body: AutoMacrosType::EnumTuple((key, items)),
})
} else {
// Sth::Sth

Ok(AutoMacros {
ident,
body: AutoMacrosType::EnumEmpty(key),
})
}
} else {
Ok(AutoMacros {
ident,
body: AutoMacrosType::Enum,
})
Err(syn::Error::new(ident.span(), "Expected { or ::"))
}
}
}
29 changes: 16 additions & 13 deletions yuuka/tests/auto_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,25 @@ mod test {

#[test]
fn basic_enum() {
derive_enum!(enum Root {
A,
B(i32),
C {
a: String,
b: i32,
},
D(enum {
E,
F(i32),
G {
derive_enum!(
#[derive(PartialEq)]
enum Root {
A,
B(i32),
C {
a: String,
b: i32,
},
})
});
D(enum {
E,
F(i32),
G {
a: String,
b: i32,
},
})
}
);

assert_eq!(auto!(Root::A), Root::A);
assert_eq!(auto!(Root::B(42)), Root::B(42));
Expand Down

0 comments on commit ab11d3c

Please sign in to comment.