1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4 parse::Parser, parse_macro_input, DeriveInput, FnArg, GenericParam, ItemFn, Lifetime,
5 LifetimeParam, PatIdent, PatType,
6};
7
8#[proc_macro_attribute]
9pub fn element(_args: TokenStream, input: TokenStream) -> TokenStream {
10 let mut ast = parse_macro_input!(input as DeriveInput);
11 let struct_name = &ast.ident;
12
13 let has_generics = !ast.generics.params.is_empty();
15
16 if ast.generics.lifetimes().count() == 0 && !has_generics {
18 ast.generics
19 .params
20 .push(syn::GenericParam::Lifetime(LifetimeParam::new(
21 Lifetime::new("'a", proc_macro2::Span::call_site()),
22 )));
23 }
24
25 match &mut ast.data {
27 syn::Data::Struct(ref mut struct_data) => {
28 if let syn::Fields::Named(fields) = &mut struct_data.fields {
29 fields.named.push(
30 syn::Field::parse_named
31 .parse2(quote! { pub children: Children })
32 .unwrap(),
33 );
34 fields.named.push(
35 syn::Field::parse_named
36 .parse2(quote! { pub style: Style })
37 .unwrap(),
38 );
39 fields.named.push(
40 syn::Field::parse_named
41 .parse2(quote! { pub id: &'a str })
42 .unwrap(),
43 );
44 fields.named.push(
45 syn::Field::parse_named
46 .parse2(quote! { pub class: &'a str })
47 .unwrap(),
48 );
49 }
50 }
51 _ => panic!("`element` can only be used with structs"),
52 }
53
54 let impl_block = quote! {
56 impl<'a> ElementCore for #struct_name<'a> {
57 fn get_element_by_id(&mut self, id: &str) -> Option<&mut Element> {
58 if let Children::Children(children, _) = &mut self.children {
59 for elem in children {
60 if elem.get_id() == id {
61 return Some(elem);
62 } else if let Some(e) = elem.get_element_by_id(id) {
63 return Some(e);
64 }
65 }
66 }
67 None
68 }
69
70 fn get_id(&self) -> String {
71 self.id.to_string()
72 }
73
74 fn get_class(&self) -> String {
75 self.class.to_string()
76 }
77
78 fn get_style(&self) -> &Style {
79 &self.style
80 }
81 }
82 };
83
84 let expanded = quote! {
85 #ast
86 #impl_block
87 };
88
89 expanded.into()
90}
91
92#[proc_macro_attribute]
93pub fn component(_: TokenStream, input: TokenStream) -> TokenStream {
94 let input_fn = parse_macro_input!(input as ItemFn);
95
96 let fn_name = input_fn.sig.ident.clone();
97 let code = input_fn.block;
98 let visibility = input_fn.vis;
99 let return_type = match input_fn.sig.output {
100 syn::ReturnType::Default => syn::parse_quote! { Element },
101 syn::ReturnType::Type(_, t) => t,
102 };
103
104 let lifetimes: Vec<_> = input_fn
105 .sig
106 .generics
107 .params
108 .iter()
109 .filter_map(|param| {
110 if let GenericParam::Lifetime(lifetime) = param {
111 Some(&lifetime.lifetime)
112 } else {
113 None
114 }
115 })
116 .collect();
117
118 let mut struct_fields = Vec::new();
119
120 for arg in input_fn.sig.inputs.iter() {
121 if let FnArg::Typed(PatType { pat, ty, .. }) = arg {
122 if let syn::Pat::Ident(PatIdent { ident, .. }) = &**pat {
123 let field_name = ident.clone();
124 let field_type = ty.clone();
125 struct_fields.push(quote! { pub #field_name: #field_type });
126 }
127 }
128 }
129
130 let expanded = quote! {
131 #[derive(Debug, Default)]
132 #[allow(non_camel_case_types)]
133 #visibility struct #fn_name<#(#lifetimes),*> {
134 #(#struct_fields),*
135 }
136
137 impl <#(#lifetimes),*> Component for #fn_name <#(#lifetimes),*> {
138 type Element = #return_type;
139 fn create_element(self) -> Self::Element {
140 #code
141 }
142 }
143 };
144
145 TokenStream::from(expanded)
146}