1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::{format_ident, quote};
4use syn::{parse_macro_input, DataStruct, DeriveInput, Field, FieldsNamed};
5use syn::{Data, Fields};
6mod helpers;
7use helpers::parse_struct_body;
8
9
10
11
12#[proc_macro_derive(YewForm, attributes(ele))]
25pub fn yew_form_derive(token_stream: TokenStream) -> TokenStream {
26 let der_input = parse_macro_input!(token_stream as DeriveInput);
35
36 let struct_name = der_input.ident;
37
38 let enum_name = format_ident!("Update{}", struct_name);
39 let provider_name = format_ident!("{}Provider", struct_name);
40 let props_name = format_ident!("{}Props", struct_name);
41 let (messages_enum,
42 struct_field_names,
43 (html_tag_type, cast_type),
44 struct_field_types) = parse_struct_body(der_input.data, &enum_name);
45 let struct_field_names_for_update = struct_field_names
47 .iter()
48 .map(|v| format_ident!("update_{}", v))
49 .collect::<Vec<proc_macro2::Ident>>();
50
51 let enum_variants = struct_field_names.iter().map(|v| format_ident!("{}", v.to_string().to_uppercase())).collect::<Vec<proc_macro2::Ident>>();
54
55 let function_component_function_name = struct_field_names.iter().map(|v| format_ident!("{}_fn",v.to_string().to_uppercase())).collect::<Vec<proc_macro2::Ident>>();
56 let function_component_names = struct_field_names.iter().map(|v| {
57 let chars = v.to_string().chars().enumerate().map(|(ind, chr)|{
58 if ind == 0 {
59 chr.to_ascii_uppercase()
60 } else {
61 chr
62 }
63 }).collect::<String>();
64 format_ident!("{}", chars)
65 }).collect::<Vec<proc_macro2::Ident>>();
66 let html_tag_type = html_tag_type.iter().map(|v| format_ident!("{}", v)).collect::<Vec<proc_macro2::Ident>>();
67
68 let update_funcs = struct_field_names.iter().map(|v| format_ident!("update_{}", v)).collect::<Vec<proc_macro2::Ident>>();
69
70 let struct_field_names_as_strings = struct_field_names.iter().map(|v| v.to_string()).collect::<Vec<String>>();
71
72 let function_component_props = function_component_names.iter().map(|v| format_ident!("{}_Props",v)).collect::<Vec<proc_macro2::Ident>>();
73
74 let generated_html = helpers::generate_html_inputs(html_tag_type.clone(), struct_field_names_as_strings.clone(), struct_field_types.clone());
78
79 let generated_callbacks = helpers::update_callbacks(struct_field_types.clone(), enum_variants.clone(), cast_type.clone(), struct_field_names.clone());
80
81 let final_output = quote! {
82
83 #messages_enum
92
93
94 #[derive(PartialEq, Properties)]
95 pub struct #props_name {
96 pub children: yew::Children
97 }
98
99
100 #[derive(Clone, PartialEq, Debug)]
109 pub struct #provider_name {
110 pub form: #struct_name,
111 #( pub #struct_field_names_for_update : yew::Callback<yew::html::onchange::Event>,)*
112 }
113
114 impl Component for #provider_name {
115 type Message = #enum_name;
116 type Properties = #props_name;
117
118 fn create(ctx: &Context<Self>) -> Self {
119
120 #(
131 let #struct_field_names_for_update = #generated_callbacks;
132 let #struct_field_names_for_update = ctx.link().callback(#struct_field_names_for_update);
133 )*
134
135
136
137
138 Self {
139 form: #struct_name {
140 #( #struct_field_names: Default::default(), )*
141 },
142 #( #struct_field_names_for_update, )*
143 }
144 }
145
146 fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
147 match msg {
148 #(Self::Message::#enum_variants(inside_value) => {
149 self.form.#struct_field_names = inside_value;
150 }
151 ),*
152 }
153 true
154 }
155
156 fn view(&self, ctx: &Context<Self>) -> Html {
157 html! {
158 <ContextProvider<#provider_name> context={self.clone()}>
159 {ctx.props().children.clone()}
160 </ContextProvider<#provider_name>>
161 }
162 }
163 }
164
165
166 #(#[derive(Properties, PartialEq)]
167 pub struct #function_component_props {
168 pub class: std::option::Option<String>
169 }
170 )*
171
172 #(#[function_component(#function_component_names)]
173 pub fn #function_component_function_name(p: &#function_component_props) -> yew::Html {
174
175 let #provider_name {
176 form: #struct_name {
177 #struct_field_names: struct_field_name,
178 ..
179 },
180 #update_funcs: update_func,
181 ..
182 } = use_context::<#provider_name>().expect("context for this field");
183
184
185
186 html! {
192 #generated_html
193 }
194 })*
198
199 }
200 .into();
201
202 final_output
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn it_works() {
213 assert!(true);
214 }
215}