From ab11d3cd02affa67b9736b1574ba801596dc1344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=8A=E6=AC=A7?= Date: Thu, 14 Nov 2024 01:11:51 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Support=20single=20level=20enums'?= =?UTF-8?q?=20`auto!`=20macro=20first.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuuka-macros/src/lib.rs | 53 +++++++++-- yuuka-macros/src/template/auto_enums.rs | 79 ++++++++++++++++- yuuka-macros/src/template/auto_structs.rs | 2 +- yuuka-macros/src/tools/auto_macros.rs | 103 ++++++++++++++++++++-- yuuka/tests/auto_macros.rs | 29 +++--- 5 files changed, 240 insertions(+), 26 deletions(-) diff --git a/yuuka-macros/src/lib.rs b/yuuka-macros/src/lib.rs index 5a5af46..6dff1e5 100644 --- a/yuuka-macros/src/lib.rs +++ b/yuuka-macros/src/lib.rs @@ -154,15 +154,58 @@ pub fn auto(input: TokenStream) -> TokenStream { }) .collect::>(); - 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::>(); + + 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::>(); + + quote! { + #ident::#key(#( #list ),*) + } + .into() + } } - _ => todo!("auto for enum"), } } diff --git a/yuuka-macros/src/template/auto_enums.rs b/yuuka-macros/src/template/auto_enums.rs index 598c629..adf3e78 100644 --- a/yuuka-macros/src/template/auto_enums.rs +++ b/yuuka-macros/src/template/auto_enums.rs @@ -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 { - 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::>(); + 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::>(); + quote! { + #(#list)* + } + } + } + }) + .collect::>(); + 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::>() } diff --git a/yuuka-macros/src/template/auto_structs.rs b/yuuka-macros/src/template/auto_structs.rs index 6e14b77..25dbd64 100644 --- a/yuuka-macros/src/template/auto_structs.rs +++ b/yuuka-macros/src/template/auto_structs.rs @@ -12,7 +12,7 @@ pub(crate) fn generate_structs_auto_macros(structs: StructsFlatten) -> Vec { + (#name { $($val:tt)+ }) => { ::yuuka::auto!(#ty { $($val)+ }) }; } diff --git a/yuuka-macros/src/tools/auto_macros.rs b/yuuka-macros/src/tools/auto_macros.rs index 686065a..a6d555b 100644 --- a/yuuka-macros/src/tools/auto_macros.rs +++ b/yuuka-macros/src/tools/auto_macros.rs @@ -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)), } #[derive(Debug, Clone)] @@ -80,11 +82,102 @@ impl Parse for AutoMacros { ident, body: AutoMacrosType::Struct(tokens), }) + } else if input.peek(Token![::]) { + input.parse::()?; + 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::()?; + + 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::()?; + } + } + + 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::()?; + } + } + + 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 ::")) } } } diff --git a/yuuka/tests/auto_macros.rs b/yuuka/tests/auto_macros.rs index 57a8f73..78f7192 100644 --- a/yuuka/tests/auto_macros.rs +++ b/yuuka/tests/auto_macros.rs @@ -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));