1use crate::CodegenError;
7use crate::expr::ast::{
8 BinaryOp, BinaryOpExpr, ConditionalExpr, Expr, FieldAccessExpr, LiteralExpr, MethodCallExpr,
9 SharedFieldAccessExpr, UnaryOp, UnaryOpExpr,
10};
11use proc_macro2::TokenStream;
12use quote::{format_ident, quote};
13
14pub fn generate_expr(expr: &Expr) -> TokenStream {
25 match expr {
26 Expr::FieldAccess(field_access) => generate_field_access(field_access),
27 Expr::SharedFieldAccess(shared_access) => generate_shared_field_access(shared_access),
28 Expr::MethodCall(method_call) => generate_method_call(method_call),
29 Expr::BinaryOp(binary_op) => generate_binary_op(binary_op),
30 Expr::UnaryOp(unary_op) => generate_unary_op(unary_op),
31 Expr::Conditional(conditional) => generate_conditional(conditional),
32 Expr::Literal(literal) => generate_literal(literal),
33 }
34}
35
36pub fn generate_bool_expr(expr: &Expr) -> TokenStream {
47 match expr {
48 Expr::FieldAccess(field_access) => generate_field_access_raw(field_access),
49 Expr::SharedFieldAccess(shared_access) => generate_shared_field_access_raw(shared_access),
50 Expr::MethodCall(method_call) => generate_method_call_raw(method_call),
51 Expr::BinaryOp(binary_op) => generate_binary_op_raw(binary_op),
52 Expr::UnaryOp(unary_op) => generate_unary_op_raw(unary_op),
53 Expr::Conditional(conditional) => generate_conditional_raw(conditional),
54 Expr::Literal(literal) => generate_literal_raw(literal),
55 }
56}
57
58pub fn validate_expression_inlinable(expr: &Expr) -> Result<(), CodegenError> {
62 match expr {
63 Expr::FieldAccess(_) => Ok(()),
64 Expr::SharedFieldAccess(_) => Ok(()), Expr::MethodCall(method_expr) => {
66 validate_expression_inlinable(&method_expr.receiver)?;
67 for arg in &method_expr.args {
68 validate_expression_inlinable(arg)?;
69 }
70 Ok(())
71 }
72 Expr::BinaryOp(binary_expr) => {
73 validate_expression_inlinable(&binary_expr.left)?;
74 validate_expression_inlinable(&binary_expr.right)?;
75 Ok(())
76 }
77 Expr::UnaryOp(unary_expr) => {
78 validate_expression_inlinable(&unary_expr.operand)?;
79 Ok(())
80 }
81 Expr::Conditional(cond_expr) => {
82 validate_expression_inlinable(&cond_expr.condition)?;
83 validate_expression_inlinable(&cond_expr.then_branch)?;
84 validate_expression_inlinable(&cond_expr.else_branch)?;
85 Ok(())
86 }
87 Expr::Literal(_) => Ok(()),
88 }
89}
90
91fn generate_field_access(expr: &FieldAccessExpr) -> TokenStream {
99 if expr.path.is_empty() {
100 return quote! { String::new() };
101 }
102
103 let field_access: Vec<_> = expr.path.iter().map(|s| format_ident!("{}", s)).collect();
104
105 quote! { model.#(#field_access).*.to_string() }
106}
107
108fn generate_shared_field_access(expr: &SharedFieldAccessExpr) -> TokenStream {
117 if expr.path.is_empty() {
118 return quote! { String::new() };
119 }
120
121 let field_access: Vec<_> = expr.path.iter().map(|s| format_ident!("{}", s)).collect();
124
125 quote! { shared.#(#field_access).*.to_string() }
126}
127
128fn generate_method_call(expr: &MethodCallExpr) -> TokenStream {
136 let receiver_tokens = generate_expr(&expr.receiver);
137 let method_ident = format_ident!("{}", expr.method);
138
139 if expr.args.is_empty() {
140 quote! { #receiver_tokens.#method_ident().to_string() }
141 } else {
142 let arg_tokens: Vec<TokenStream> = expr.args.iter().map(generate_expr).collect();
143 quote! { #receiver_tokens.#method_ident(#(#arg_tokens),*).to_string() }
144 }
145}
146
147fn generate_binary_op(expr: &BinaryOpExpr) -> TokenStream {
155 let left = generate_expr(&expr.left);
156 let right = generate_expr(&expr.right);
157 let op = match expr.op {
158 BinaryOp::Eq => quote! { == },
159 BinaryOp::Ne => quote! { != },
160 BinaryOp::Lt => quote! { < },
161 BinaryOp::Le => quote! { <= },
162 BinaryOp::Gt => quote! { > },
163 BinaryOp::Ge => quote! { >= },
164 BinaryOp::And => quote! { && },
165 BinaryOp::Or => quote! { || },
166 BinaryOp::Add => quote! { + },
167 BinaryOp::Sub => quote! { - },
168 BinaryOp::Mul => quote! { * },
169 BinaryOp::Div => quote! { / },
170 };
171
172 quote! { (#left #op #right).to_string() }
173}
174
175fn generate_unary_op(expr: &UnaryOpExpr) -> TokenStream {
183 let operand = generate_expr(&expr.operand);
184 let op = match expr.op {
185 UnaryOp::Not => quote! { ! },
186 UnaryOp::Neg => quote! { - },
187 };
188
189 quote! { (#op #operand).to_string() }
190}
191
192fn generate_conditional(expr: &ConditionalExpr) -> TokenStream {
200 let condition = generate_expr(&expr.condition);
201 let then_branch = generate_expr(&expr.then_branch);
202 let else_branch = generate_expr(&expr.else_branch);
203
204 quote! {
205 {
206 let __cond = #condition;
207 let __then = #then_branch;
208 let __else = #else_branch;
209 if __cond.trim() == "true" || __cond.parse::<bool>().unwrap_or(false) {
210 __then
211 } else {
212 __else
213 }
214 }
215 }
216}
217
218fn generate_literal(expr: &LiteralExpr) -> TokenStream {
226 match expr {
227 LiteralExpr::String(s) => {
228 let lit = proc_macro2::Literal::string(s);
229 quote! { #lit.to_string() }
230 }
231 LiteralExpr::Integer(n) => {
232 let lit = proc_macro2::Literal::i64_unsuffixed(*n);
233 quote! { #lit.to_string() }
234 }
235 LiteralExpr::Float(f) => {
236 let lit = proc_macro2::Literal::f64_unsuffixed(*f);
237 quote! { #lit.to_string() }
238 }
239 LiteralExpr::Bool(b) => {
240 let val = if *b { "true" } else { "false" };
241 let lit = proc_macro2::Literal::string(val);
242 quote! { #lit.to_string() }
243 }
244 }
245}
246
247pub fn generate_interpolated(parts: &[String]) -> TokenStream {
263 if parts.is_empty() {
264 return quote! { String::new() };
265 }
266
267 let mut format_args = Vec::new();
268 let mut arg_exprs = Vec::new();
269
270 for part in parts {
271 if part.starts_with('{') && part.ends_with('}') {
272 let binding_name = &part[1..part.len() - 1];
273 let field_parts: Vec<_> = binding_name
274 .split('.')
275 .map(|s| format_ident!("{}", s))
276 .collect();
277 format_args.push("{}");
278 arg_exprs.push(quote! { #(#field_parts).*.to_string() });
279 } else {
280 format_args.push(part);
281 }
282 }
283
284 let format_string = format_args.join("");
285 let lit = proc_macro2::Literal::string(&format_string);
286
287 quote! { format!(#lit, #(#arg_exprs),*) }
288}
289
290fn generate_field_access_raw(expr: &FieldAccessExpr) -> TokenStream {
297 generate_field_access_raw_with_locals(expr, &std::collections::HashSet::new())
298}
299
300fn generate_field_access_raw_with_locals(
302 expr: &FieldAccessExpr,
303 local_vars: &std::collections::HashSet<String>,
304) -> TokenStream {
305 if expr.path.is_empty() {
306 return quote! { false };
307 }
308
309 if let Some(first) = expr.path.first()
311 && local_vars.contains(first)
312 {
313 let field_access: Vec<_> = expr.path.iter().map(|s| format_ident!("{}", s)).collect();
315 return quote! { #(#field_access).* };
316 }
317
318 let field_access: Vec<_> = expr.path.iter().map(|s| format_ident!("{}", s)).collect();
319 quote! { model.#(#field_access).* }
320}
321
322pub fn generate_bool_expr_with_locals(
324 expr: &Expr,
325 local_vars: &std::collections::HashSet<String>,
326) -> TokenStream {
327 match expr {
328 Expr::FieldAccess(field_access) => {
329 generate_field_access_raw_with_locals(field_access, local_vars)
330 }
331 Expr::SharedFieldAccess(shared_access) => generate_shared_field_access_raw(shared_access),
332 Expr::MethodCall(method_call) => {
333 generate_method_call_raw_with_locals(method_call, local_vars)
334 }
335 Expr::BinaryOp(binary_op) => generate_binary_op_raw_with_locals(binary_op, local_vars),
336 Expr::UnaryOp(unary_op) => generate_unary_op_raw_with_locals(unary_op, local_vars),
337 Expr::Conditional(conditional) => {
338 generate_conditional_raw_with_locals(conditional, local_vars)
339 }
340 Expr::Literal(literal) => generate_literal_raw(literal),
341 }
342}
343
344pub fn generate_expr_with_locals(
346 expr: &Expr,
347 local_vars: &std::collections::HashSet<String>,
348) -> TokenStream {
349 match expr {
350 Expr::FieldAccess(field_access) => {
351 generate_field_access_with_locals(field_access, local_vars)
352 }
353 Expr::SharedFieldAccess(shared_access) => generate_shared_field_access(shared_access),
354 Expr::MethodCall(method_call) => generate_method_call_with_locals(method_call, local_vars),
355 Expr::BinaryOp(binary_op) => generate_binary_op_with_locals(binary_op, local_vars),
356 Expr::UnaryOp(unary_op) => generate_unary_op_with_locals(unary_op, local_vars),
357 Expr::Conditional(conditional) => generate_conditional_with_locals(conditional, local_vars),
358 Expr::Literal(literal) => generate_literal(literal),
359 }
360}
361
362fn generate_field_access_with_locals(
364 expr: &FieldAccessExpr,
365 local_vars: &std::collections::HashSet<String>,
366) -> TokenStream {
367 if expr.path.is_empty() {
368 return quote! { String::new() };
369 }
370
371 if let Some(first) = expr.path.first()
373 && local_vars.contains(first)
374 {
375 let field_access: Vec<_> = expr.path.iter().map(|s| format_ident!("{}", s)).collect();
377 return quote! { #(#field_access).*.to_string() };
378 }
379
380 let field_access: Vec<_> = expr.path.iter().map(|s| format_ident!("{}", s)).collect();
381 quote! { model.#(#field_access).*.to_string() }
382}
383
384fn generate_method_call_with_locals(
385 expr: &MethodCallExpr,
386 local_vars: &std::collections::HashSet<String>,
387) -> TokenStream {
388 let receiver_tokens = generate_expr_with_locals(&expr.receiver, local_vars);
389 let method_ident = format_ident!("{}", expr.method);
390 let arg_tokens: Vec<_> = expr
391 .args
392 .iter()
393 .map(|a| generate_expr_with_locals(a, local_vars))
394 .collect();
395
396 if arg_tokens.is_empty() {
397 quote! { #receiver_tokens.#method_ident().to_string() }
398 } else {
399 quote! { #receiver_tokens.#method_ident(#(#arg_tokens),*).to_string() }
400 }
401}
402
403fn generate_binary_op_with_locals(
404 expr: &BinaryOpExpr,
405 local_vars: &std::collections::HashSet<String>,
406) -> TokenStream {
407 let left = generate_expr_with_locals(&expr.left, local_vars);
408 let right = generate_expr_with_locals(&expr.right, local_vars);
409 let op = match expr.op {
410 BinaryOp::Eq => quote! { == },
411 BinaryOp::Ne => quote! { != },
412 BinaryOp::Lt => quote! { < },
413 BinaryOp::Le => quote! { <= },
414 BinaryOp::Gt => quote! { > },
415 BinaryOp::Ge => quote! { >= },
416 BinaryOp::And => quote! { && },
417 BinaryOp::Or => quote! { || },
418 BinaryOp::Add => quote! { + },
419 BinaryOp::Sub => quote! { - },
420 BinaryOp::Mul => quote! { * },
421 BinaryOp::Div => quote! { / },
422 };
423
424 quote! { (#left #op #right).to_string() }
425}
426
427fn generate_unary_op_with_locals(
428 expr: &UnaryOpExpr,
429 local_vars: &std::collections::HashSet<String>,
430) -> TokenStream {
431 let operand = generate_expr_with_locals(&expr.operand, local_vars);
432 let op = match expr.op {
433 UnaryOp::Not => quote! { ! },
434 UnaryOp::Neg => quote! { - },
435 };
436
437 quote! { (#op #operand).to_string() }
438}
439
440fn generate_conditional_with_locals(
441 expr: &ConditionalExpr,
442 local_vars: &std::collections::HashSet<String>,
443) -> TokenStream {
444 let condition = generate_expr_with_locals(&expr.condition, local_vars);
445 let then_branch = generate_expr_with_locals(&expr.then_branch, local_vars);
446 let else_branch = generate_expr_with_locals(&expr.else_branch, local_vars);
447
448 quote! {
449 {
450 let __cond = #condition;
451 let __then = #then_branch;
452 let __else = #else_branch;
453 if __cond.trim() == "true" || __cond.parse::<bool>().unwrap_or(false) {
454 __then
455 } else {
456 __else
457 }
458 }
459 }
460}
461
462fn generate_method_call_raw_with_locals(
463 expr: &MethodCallExpr,
464 local_vars: &std::collections::HashSet<String>,
465) -> TokenStream {
466 let receiver_tokens = generate_bool_expr_with_locals(&expr.receiver, local_vars);
467 let method_ident = format_ident!("{}", &expr.method);
468 let arg_tokens: Vec<_> = expr
469 .args
470 .iter()
471 .map(|a| generate_bool_expr_with_locals(a, local_vars))
472 .collect();
473
474 quote! { #receiver_tokens.#method_ident(#(#arg_tokens),*) }
475}
476
477fn generate_binary_op_raw_with_locals(
478 expr: &BinaryOpExpr,
479 local_vars: &std::collections::HashSet<String>,
480) -> TokenStream {
481 let left = generate_bool_expr_with_locals(&expr.left, local_vars);
482 let right = generate_bool_expr_with_locals(&expr.right, local_vars);
483 let op = match expr.op {
484 BinaryOp::Eq => quote! { == },
485 BinaryOp::Ne => quote! { != },
486 BinaryOp::Lt => quote! { < },
487 BinaryOp::Le => quote! { <= },
488 BinaryOp::Gt => quote! { > },
489 BinaryOp::Ge => quote! { >= },
490 BinaryOp::And => quote! { && },
491 BinaryOp::Or => quote! { || },
492 BinaryOp::Add => quote! { + },
493 BinaryOp::Sub => quote! { - },
494 BinaryOp::Mul => quote! { * },
495 BinaryOp::Div => quote! { / },
496 };
497
498 quote! { #left #op #right }
499}
500
501fn generate_unary_op_raw_with_locals(
502 expr: &UnaryOpExpr,
503 local_vars: &std::collections::HashSet<String>,
504) -> TokenStream {
505 let operand = generate_bool_expr_with_locals(&expr.operand, local_vars);
506 let op = match expr.op {
507 UnaryOp::Not => quote! { ! },
508 UnaryOp::Neg => quote! { - },
509 };
510
511 quote! { #op #operand }
512}
513
514fn generate_conditional_raw_with_locals(
515 expr: &ConditionalExpr,
516 local_vars: &std::collections::HashSet<String>,
517) -> TokenStream {
518 let condition = generate_bool_expr_with_locals(&expr.condition, local_vars);
519 let then_branch = generate_bool_expr_with_locals(&expr.then_branch, local_vars);
520 let else_branch = generate_bool_expr_with_locals(&expr.else_branch, local_vars);
521
522 quote! {
523 if #condition {
524 #then_branch
525 } else {
526 #else_branch
527 }
528 }
529}
530
531fn generate_shared_field_access_raw(expr: &SharedFieldAccessExpr) -> TokenStream {
533 if expr.path.is_empty() {
534 return quote! { false };
535 }
536
537 let field_access: Vec<_> = expr.path.iter().map(|s| format_ident!("{}", s)).collect();
538 quote! { shared.#(#field_access).* }
539}
540
541fn generate_method_call_raw(expr: &MethodCallExpr) -> TokenStream {
543 let receiver_tokens = generate_bool_expr(&expr.receiver);
544 let method_ident = format_ident!("{}", &expr.method);
545 let arg_tokens: Vec<_> = expr.args.iter().map(generate_bool_expr).collect();
546
547 quote! { #receiver_tokens.#method_ident(#(#arg_tokens),*) }
548}
549
550fn generate_binary_op_raw(expr: &BinaryOpExpr) -> TokenStream {
552 let left = generate_bool_expr(&expr.left);
553 let right = generate_bool_expr(&expr.right);
554 let op = match expr.op {
555 BinaryOp::Eq => quote! { == },
556 BinaryOp::Ne => quote! { != },
557 BinaryOp::Lt => quote! { < },
558 BinaryOp::Le => quote! { <= },
559 BinaryOp::Gt => quote! { > },
560 BinaryOp::Ge => quote! { >= },
561 BinaryOp::And => quote! { && },
562 BinaryOp::Or => quote! { || },
563 BinaryOp::Add => quote! { + },
564 BinaryOp::Sub => quote! { - },
565 BinaryOp::Mul => quote! { * },
566 BinaryOp::Div => quote! { / },
567 };
568
569 quote! { #left #op #right }
570}
571
572fn generate_unary_op_raw(expr: &UnaryOpExpr) -> TokenStream {
574 let operand = generate_bool_expr(&expr.operand);
575 let op = match expr.op {
576 UnaryOp::Not => quote! { ! },
577 UnaryOp::Neg => quote! { - },
578 };
579
580 quote! { #op #operand }
581}
582
583fn generate_conditional_raw(expr: &ConditionalExpr) -> TokenStream {
585 let condition = generate_bool_expr(&expr.condition);
586 let then_branch = generate_bool_expr(&expr.then_branch);
587 let else_branch = generate_bool_expr(&expr.else_branch);
588
589 quote! {
590 if #condition {
591 #then_branch
592 } else {
593 #else_branch
594 }
595 }
596}
597
598fn generate_literal_raw(expr: &LiteralExpr) -> TokenStream {
600 match expr {
601 LiteralExpr::String(s) => {
602 let lit = proc_macro2::Literal::string(s);
603 quote! { #lit }
604 }
605 LiteralExpr::Integer(n) => {
606 let lit = proc_macro2::Literal::i64_unsuffixed(*n);
607 quote! { #lit }
608 }
609 LiteralExpr::Float(f) => {
610 let lit = proc_macro2::Literal::f64_unsuffixed(*f);
611 quote! { #lit }
612 }
613 LiteralExpr::Bool(b) => {
614 quote! { #b }
615 }
616 }
617}