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 generate_handler_simple(
74 handler_name: &str,
75 _model_ident: &syn::Ident,
76 message_ident: &syn::Ident,
77 _returns_command: bool,
78) -> TokenStream {
79 let handler_ident = format_ident!("{}", handler_name);
80 quote! {
81 #handler_name => {
82 #message_ident::#handler_ident
83 }
84 }
85}
86
87fn generate_handler_with_value(
98 handler_name: &str,
99 value_type: &str,
100 _model_ident: &syn::Ident,
101 message_ident: &syn::Ident,
102) -> TokenStream {
103 let handler_ident = format_ident!("{}", handler_name);
104 let type_ident = format_ident!("{}", value_type);
105 quote! {
106 #handler_name => {
107 |value: #type_ident| #message_ident::#handler_ident(value)
108 }
109 }
110}
111
112fn generate_handler_with_command(
123 handler_name: &str,
124 value_type: &str,
125 _model_ident: &syn::Ident,
126 message_ident: &syn::Ident,
127) -> TokenStream {
128 let handler_ident = format_ident!("{}", handler_name);
129 let type_ident = format_ident!("{}", value_type);
130 quote! {
131 #handler_name => {
132 |value: #type_ident| #message_ident::#handler_ident(value)
133 }
134 }
135}
136
137pub fn generate_update_function(
147 handlers: &[HandlerSignature],
148 model_name: &str,
149 message_name: &str,
150) -> Result<TokenStream, CodegenError> {
151 let message_ident = format_ident!("{}", message_name);
152
153 if handlers.is_empty() {
154 return Ok(quote! {
155 fn update(&mut self, _message: Self::Message) {}
156 });
157 }
158
159 let arms: Vec<TokenStream> = handlers
160 .iter()
161 .map(|handler| {
162 let _model_ident = format_ident!("{}", model_name);
163 let _handler_ident = format_ident!("{}", handler.name);
164 let handler_name_str = handler.name.clone();
165
166 match (&handler.param_type, handler.returns_command) {
167 (None, false) => {
168 quote! {
169 #message_ident::#_handler_ident => {
170 self.handler_registry.call_simple(&mut self.model, #handler_name_str);
171 }
172 }
173 }
174 (Some(param_type), false) => {
175 let type_ident = format_ident!("{}", param_type);
176 quote! {
177 #message_ident::#_handler_ident(value) => {
178 self.handler_registry.call_with_value::<#type_ident>(&mut self.model, #handler_name_str, value);
179 }
180 }
181 }
182 (None, true) => {
183 quote! {
184 #message_ident::#_handler_ident => {
185 if let Some(cmd) = self.handler_registry.call_with_command(&mut self.model, #handler_name_str) {
186 return cmd;
187 }
188 }
189 }
190 }
191 (Some(param_type), true) => {
192 let type_ident = format_ident!("{}", param_type);
193 quote! {
194 #message_ident::#_handler_ident(value) => {
195 if let Some(cmd) = self.handler_registry.call_with_command::<#type_ident>(&mut self.model, #handler_name_str, value) {
196 return cmd;
197 }
198 }
199 }
200 }
201 }
202 })
203 .collect();
204
205 let catch_all = quote! {
206 _ => {}
207 };
208
209 Ok(quote! {
210 fn update(&mut self, message: Self::Message) -> Option<iced::Command<Self::Message>> {
211 match message {
212 #(#arms)*
213 #catch_all
214 }
215 None
216 }
217 })
218}
219
220pub fn validate_expression_inlinable(expr: &crate::Expr) -> Result<(), CodegenError> {
224 match expr {
225 crate::Expr::FieldAccess(_) => Ok(()),
226 crate::Expr::MethodCall(method_expr) => {
227 validate_expression_inlinable(&method_expr.receiver)?;
228 for arg in &method_expr.args {
229 validate_expression_inlinable(arg)?;
230 }
231 Ok(())
232 }
233 crate::Expr::BinaryOp(binary_expr) => {
234 validate_expression_inlinable(&binary_expr.left)?;
235 validate_expression_inlinable(&binary_expr.right)?;
236 Ok(())
237 }
238 crate::Expr::UnaryOp(unary_expr) => {
239 validate_expression_inlinable(&unary_expr.operand)?;
240 Ok(())
241 }
242 crate::Expr::Conditional(cond_expr) => {
243 validate_expression_inlinable(&cond_expr.condition)?;
244 validate_expression_inlinable(&cond_expr.then_branch)?;
245 validate_expression_inlinable(&cond_expr.else_branch)?;
246 Ok(())
247 }
248 crate::Expr::Literal(_) => Ok(()),
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 #[test]
257 fn test_handler_dispatch_simple() {
258 let handlers = vec![HandlerSignature {
259 name: "increment".to_string(),
260 param_type: None,
261 returns_command: false,
262 }];
263
264 let result = generate_handler_dispatch(&handlers, "Model", "Message").unwrap();
265 let code = result.to_string();
266 assert!(code.contains("increment"));
267 }
268
269 #[test]
270 fn test_handler_dispatch_with_value() {
271 let handlers = vec![HandlerSignature {
272 name: "set_value".to_string(),
273 param_type: Some("String".to_string()),
274 returns_command: false,
275 }];
276
277 let result = generate_handler_dispatch(&handlers, "Model", "Message").unwrap();
278 let code = result.to_string();
279 assert!(code.contains("set_value"));
280 assert!(code.contains("String"));
281 }
282
283 #[test]
284 fn test_handler_dispatch_with_command() {
285 let handlers = vec![HandlerSignature {
286 name: "save".to_string(),
287 param_type: None,
288 returns_command: true,
289 }];
290
291 let result = generate_handler_dispatch(&handlers, "Model", "Message").unwrap();
292 let code = result.to_string();
293 assert!(code.contains("save"));
294 }
295
296 #[test]
297 fn test_update_function_generation() {
298 let handlers = vec![
299 HandlerSignature {
300 name: "increment".to_string(),
301 param_type: None,
302 returns_command: false,
303 },
304 HandlerSignature {
305 name: "set_value".to_string(),
306 param_type: Some("String".to_string()),
307 returns_command: false,
308 },
309 ];
310
311 let result = generate_update_function(&handlers, "Model", "Message").unwrap();
312 let code = result.to_string();
313 assert!(code.contains("update"));
314 assert!(code.contains("increment"));
315 assert!(code.contains("set_value"));
316 }
317
318 #[test]
319 fn test_expression_validation() {
320 use crate::expr::ast::{BinaryOp, BinaryOpExpr, Expr, FieldAccessExpr, LiteralExpr};
321
322 let expr = Expr::BinaryOp(BinaryOpExpr {
323 left: Box::new(Expr::FieldAccess(FieldAccessExpr {
324 path: vec!["count".to_string()],
325 })),
326 op: BinaryOp::Add,
327 right: Box::new(Expr::Literal(LiteralExpr::Integer(1))),
328 });
329
330 assert!(validate_expression_inlinable(&expr).is_ok());
331 }
332}