1extern crate proc_macro;
7
8use proc_macro::TokenStream;
9use quote::quote;
10use syn::{
11 braced,
12 parse::{Parse, ParseStream},
13 parse_macro_input, FnArg, Ident, ImplItem, Token, Type,
14};
15
16struct Class {
17 name: Ident,
18 fields: Vec<(Ident, Type)>,
19 methods: Vec<ImplItem>,
20}
21
22struct Classes {
23 classes: Vec<Class>,
24}
25
26impl Parse for Class {
27 fn parse(input: ParseStream) -> syn::Result<Self> {
28 let name: Ident = input.parse()?;
29 let content;
30 braced!(content in input);
31
32 let mut fields = Vec::new();
33 let mut methods = Vec::new();
34
35 while !content.is_empty() {
36 if content.peek(Token![fn]) {
37 let method: ImplItem = content.parse()?;
38 methods.push(method);
39 } else {
40 let ident: Ident = content.parse()?;
41 content.parse::<Token![:]>()?;
42 let ty: Type = content.parse()?;
43 if content.peek(Token![,]) {
44 content.parse::<Token![,]>()?;
45 } else {
46 content.parse::<Token![;]>().ok();
47 }
48 fields.push((ident, ty));
49 }
50 }
51
52 Ok(Class { name, fields, methods })
53 }
54}
55
56impl Parse for Classes {
57 fn parse(input: ParseStream) -> syn::Result<Self> {
58 let mut classes = Vec::new();
59
60 while !input.is_empty() {
61 let class: Class = input.parse()?;
62 classes.push(class);
63
64 if input.peek(Token![,]) {
65 input.parse::<Token![,]>()?;
66 }
67 }
68
69 Ok(Classes { classes })
70 }
71}
72
73#[proc_macro]
74pub fn class(input: TokenStream) -> TokenStream {
75 let classes = parse_macro_input!(input as Classes);
76
77 let class_defs = classes.classes.iter().map(|class| {
78 let name = &class.name;
79 let fields = &class.fields;
80 let methods = &class.methods;
81
82 let field_defs = fields.iter().map(|(ident, ty)| {
83 quote! {
84 pub #ident: #ty,
85 }
86 });
87
88 let method_defs = methods.iter().map(|method| {
89 if let ImplItem::Fn(mut method_fn) = method.clone() {
90 let method_name = &method_fn.sig.ident;
91
92 if method_name.to_string().to_lowercase() != "new" {
93 if !method_fn.sig.inputs.iter().any(|arg| matches!(arg, FnArg::Receiver(_))) {
94 method_fn.sig.inputs.insert(0, syn::parse_quote!( &self ));
95 }
96 }
97
98 quote! {
99 #method_fn
100 }
101 } else {
102 quote! {}
103 }
104 });
105
106 quote! {
107 pub struct #name {
108 #(#field_defs)*
109 }
110
111 impl #name {
112 #(#method_defs)*
113 }
114 }
115 });
116
117 let expanded = quote! {
118 #(#class_defs)*
119 };
120
121 TokenStream::from(expanded)
122}