1use crate::CodegenError;
7use crate::handler::HandlerSignature;
8use proc_macro2::TokenStream;
9use quote::{format_ident, quote};
10
11pub fn generate_handler_dispatch(
24 handlers: &[HandlerSignature],
25 model_name: &str,
26 message_name: &str,
27) -> Result<TokenStream, CodegenError> {
28 if handlers.is_empty() {
29 return Ok(quote! {
30 match _handler {
31 _ => {}
32 }
33 });
34 }
35
36 let model_ident = format_ident!("{}", model_name);
37 let message_ident = format_ident!("{}", message_name);
38
39 let match_arms: Vec<TokenStream> = handlers
40 .iter()
41 .map(move |handler| {
42 let _handler_ident = format_ident!("{}", handler.name);
43
44 match &handler.param_type {
45 Some(param_type) if handler.returns_command => generate_handler_with_command(
46 &handler.name,
47 param_type,
48 &model_ident,
49 &message_ident,
50 ),
51 Some(param_type) => generate_handler_with_value(
52 &handler.name,
53 param_type,
54 &model_ident,
55 &message_ident,
56 ),
57 None if handler.returns_command => {
58 generate_handler_simple(&handler.name, &model_ident, &message_ident, true)
59 }
60 None => generate_handler_simple(&handler.name, &model_ident, &message_ident, false),
61 }
62 })
63 .collect();
64
65 Ok(quote! {
66 match handler_name {
67 #(#match_arms),*
68 }
69 })
70}
71
72fn to_upper_camel_case(s: &str) -> String {
74 let mut result = String::new();
75 let mut capitalize_next = true;
76 for c in s.chars() {
77 if c == '_' {
78 capitalize_next = true;
79 } else if capitalize_next {
80 result.push(c.to_ascii_uppercase());
81 capitalize_next = false;
82 } else {
83 result.push(c);
84 }
85 }
86 result
87}
88
89fn generate_handler_simple(
91 handler_name: &str,
92 _model_ident: &syn::Ident,
93 message_ident: &syn::Ident,
94 _returns_command: bool,
95) -> TokenStream {
96 let variant_name = to_upper_camel_case(handler_name);
97 let handler_ident = format_ident!("{}", variant_name);
98 quote! {
99 #handler_name => {
100 #message_ident::#handler_ident
101 }
102 }
103}
104
105fn generate_handler_with_value(
116 handler_name: &str,
117 value_type: &str,
118 _model_ident: &syn::Ident,
119 message_ident: &syn::Ident,
120) -> TokenStream {
121 let variant_name = to_upper_camel_case(handler_name);
122 let handler_ident = format_ident!("{}", variant_name);
123 let type_ident = format_ident!("{}", value_type);
124 quote! {
125 #handler_name => {
126 |value: #type_ident| #message_ident::#handler_ident(value)
127 }
128 }
129}
130
131fn generate_handler_with_command(
142 handler_name: &str,
143 value_type: &str,
144 _model_ident: &syn::Ident,
145 message_ident: &syn::Ident,
146) -> TokenStream {
147 let variant_name = to_upper_camel_case(handler_name);
148 let handler_ident = format_ident!("{}", variant_name);
149 let type_ident = format_ident!("{}", value_type);
150 quote! {
151 #handler_name => {
152 |value: #type_ident| #message_ident::#handler_ident(value)
153 }
154 }
155}
156
157pub fn generate_update_function(
167 handlers: &[HandlerSignature],
168 model_name: &str,
169 message_name: &str,
170) -> Result<TokenStream, CodegenError> {
171 let message_ident = format_ident!("{}", message_name);
172
173 if handlers.is_empty() {
174 return Ok(quote! {
175 fn update(&mut self, _message: Self::Message) {}
176 });
177 }
178
179 let arms: Vec<TokenStream> = handlers
180 .iter()
181 .map(|handler| {
182 let _model_ident = format_ident!("{}", model_name);
183 let variant_name = to_upper_camel_case(&handler.name);
184 let _handler_ident = format_ident!("{}", variant_name);
185 let handler_name_str = handler.name.clone();
186
187 match (&handler.param_type, handler.returns_command) {
188 (None, false) => {
189 quote! {
190 #message_ident::#_handler_ident => {
191 self.handler_registry.call_simple(&mut self.model, #handler_name_str);
192 }
193 }
194 }
195 (Some(param_type), false) => {
196 let type_ident = format_ident!("{}", param_type);
197 quote! {
198 #message_ident::#_handler_ident(value) => {
199 self.handler_registry.call_with_value::<#type_ident>(&mut self.model, #handler_name_str, value);
200 }
201 }
202 }
203 (None, true) => {
204 quote! {
205 #message_ident::#_handler_ident => {
206 if let Some(cmd) = self.handler_registry.call_with_command(&mut self.model, #handler_name_str) {
207 return cmd;
208 }
209 }
210 }
211 }
212 (Some(param_type), true) => {
213 let type_ident = format_ident!("{}", param_type);
214 quote! {
215 #message_ident::#_handler_ident(value) => {
216 if let Some(cmd) = self.handler_registry.call_with_command::<#type_ident>(&mut self.model, #handler_name_str, value) {
217 return cmd;
218 }
219 }
220 }
221 }
222 }
223 })
224 .collect();
225
226 let catch_all = quote! {
227 _ => {}
228 };
229
230 Ok(quote! {
231 fn update(&mut self, message: Self::Message) -> Option<iced::Command<Self::Message>> {
232 match message {
233 #(#arms)*
234 #catch_all
235 }
236 None
237 }
238 })
239}
240
241pub fn validate_expression_inlinable(expr: &crate::Expr) -> Result<(), CodegenError> {
245 match expr {
246 crate::Expr::FieldAccess(_) => Ok(()),
247 crate::Expr::SharedFieldAccess(_) => Ok(()), crate::Expr::MethodCall(method_expr) => {
249 validate_expression_inlinable(&method_expr.receiver)?;
250 for arg in &method_expr.args {
251 validate_expression_inlinable(arg)?;
252 }
253 Ok(())
254 }
255 crate::Expr::BinaryOp(binary_expr) => {
256 validate_expression_inlinable(&binary_expr.left)?;
257 validate_expression_inlinable(&binary_expr.right)?;
258 Ok(())
259 }
260 crate::Expr::UnaryOp(unary_expr) => {
261 validate_expression_inlinable(&unary_expr.operand)?;
262 Ok(())
263 }
264 crate::Expr::Conditional(cond_expr) => {
265 validate_expression_inlinable(&cond_expr.condition)?;
266 validate_expression_inlinable(&cond_expr.then_branch)?;
267 validate_expression_inlinable(&cond_expr.else_branch)?;
268 Ok(())
269 }
270 crate::Expr::Literal(_) => Ok(()),
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn test_handler_dispatch_simple() {
280 let handlers = vec![HandlerSignature {
281 name: "increment".to_string(),
282 param_type: None,
283 returns_command: false,
284 }];
285
286 let result = generate_handler_dispatch(&handlers, "Model", "Message").unwrap();
287 let code = result.to_string();
288 assert!(code.contains("increment"));
289 }
290
291 #[test]
292 fn test_handler_dispatch_with_value() {
293 let handlers = vec![HandlerSignature {
294 name: "set_value".to_string(),
295 param_type: Some("String".to_string()),
296 returns_command: false,
297 }];
298
299 let result = generate_handler_dispatch(&handlers, "Model", "Message").unwrap();
300 let code = result.to_string();
301 assert!(code.contains("set_value"));
302 assert!(code.contains("String"));
303 }
304
305 #[test]
306 fn test_handler_dispatch_with_command() {
307 let handlers = vec![HandlerSignature {
308 name: "save".to_string(),
309 param_type: None,
310 returns_command: true,
311 }];
312
313 let result = generate_handler_dispatch(&handlers, "Model", "Message").unwrap();
314 let code = result.to_string();
315 assert!(code.contains("save"));
316 }
317
318 #[test]
319 fn test_update_function_generation() {
320 let handlers = vec![
321 HandlerSignature {
322 name: "increment".to_string(),
323 param_type: None,
324 returns_command: false,
325 },
326 HandlerSignature {
327 name: "set_value".to_string(),
328 param_type: Some("String".to_string()),
329 returns_command: false,
330 },
331 ];
332
333 let result = generate_update_function(&handlers, "Model", "Message").unwrap();
334 let code = result.to_string();
335 assert!(code.contains("update"));
336 assert!(code.contains("increment"));
337 assert!(code.contains("set_value"));
338 }
339
340 #[test]
341 fn test_expression_validation() {
342 use crate::expr::ast::{BinaryOp, BinaryOpExpr, Expr, FieldAccessExpr, LiteralExpr};
343
344 let expr = Expr::BinaryOp(BinaryOpExpr {
345 left: Box::new(Expr::FieldAccess(FieldAccessExpr {
346 path: vec!["count".to_string()],
347 })),
348 op: BinaryOp::Add,
349 right: Box::new(Expr::Literal(LiteralExpr::Integer(1))),
350 });
351
352 assert!(validate_expression_inlinable(&expr).is_ok());
353 }
354}