1use crate::binding::{BindingValue, UiBindable};
4use crate::expr::error::{BindingError, BindingErrorKind};
5use crate::expr::{
6 BinaryOp, BinaryOpExpr, ConditionalExpr, Expr, FieldAccessExpr, LiteralExpr, MethodCallExpr,
7 SharedFieldAccessExpr, UnaryOp, UnaryOpExpr,
8};
9
10pub fn evaluate_expr(expr: &Expr, model: &dyn UiBindable) -> Result<BindingValue, BindingError> {
12 match expr {
13 Expr::FieldAccess(field_expr) => evaluate_field_access(field_expr, model),
14 Expr::SharedFieldAccess(_) => {
15 Err(BindingError {
17 kind: BindingErrorKind::InvalidOperation,
18 message: "Shared field access requires shared context. Use evaluate_expr_with_shared instead.".to_string(),
19 span: crate::ir::span::Span::new(0, 0, 0, 0),
20 suggestion: None,
21 })
22 }
23 Expr::MethodCall(method_expr) => evaluate_method_call(method_expr, model),
24 Expr::BinaryOp(binary_expr) => evaluate_binary_op(binary_expr, model),
25 Expr::UnaryOp(unary_expr) => evaluate_unary_op(unary_expr, model),
26 Expr::Conditional(conditional_expr) => evaluate_conditional(conditional_expr, model),
27 Expr::Literal(literal_expr) => Ok(evaluate_literal(literal_expr)),
28 }
29}
30
31fn evaluate_field_access(
33 field_expr: &FieldAccessExpr,
34 model: &dyn UiBindable,
35) -> Result<BindingValue, BindingError> {
36 let path: Vec<&str> = field_expr.path.iter().map(|s| s.as_str()).collect();
37
38 model.get_field(&path).ok_or_else(|| {
39 let field_name = field_expr.path.join(".");
40 BindingError {
41 kind: BindingErrorKind::UnknownField,
42 message: format!("Field '{}' not found", field_name),
43 span: crate::ir::span::Span::new(0, 0, 0, 0), suggestion: None,
45 }
46 })
47}
48
49fn evaluate_method_call(
51 method_expr: &MethodCallExpr,
52 model: &dyn UiBindable,
53) -> Result<BindingValue, BindingError> {
54 let receiver = evaluate_expr(&method_expr.receiver, model)?;
55 let method = &method_expr.method;
56
57 let _args: Vec<BindingValue> = method_expr
59 .args
60 .iter()
61 .map(|arg| evaluate_expr(arg, model))
62 .collect::<Result<Vec<_>, _>>()?;
63
64 match (receiver.clone(), method.as_str()) {
65 (BindingValue::String(s), "len") => Ok(BindingValue::Integer(s.len() as i64)),
67 (BindingValue::String(s), "to_uppercase") => Ok(BindingValue::String(s.to_uppercase())),
68 (BindingValue::String(s), "to_lowercase") => Ok(BindingValue::String(s.to_lowercase())),
69 (BindingValue::String(s), "trim") => Ok(BindingValue::String(s.trim().to_string())),
70
71 (BindingValue::List(l), "len") => Ok(BindingValue::Integer(l.len() as i64)),
73 (BindingValue::List(l), "is_empty") => Ok(BindingValue::Bool(l.is_empty())),
74
75 (BindingValue::Integer(i), "to_string") => Ok(BindingValue::String(i.to_string())),
77
78 (BindingValue::Float(f), "to_string") => Ok(BindingValue::String(f.to_string())),
80 (BindingValue::Float(f), "round") => Ok(BindingValue::Float(f.round())),
81 (BindingValue::Float(f), "floor") => Ok(BindingValue::Float(f.floor())),
82 (BindingValue::Float(f), "ceil") => Ok(BindingValue::Float(f.ceil())),
83
84 (BindingValue::Bool(b), "to_string") => Ok(BindingValue::String(b.to_string())),
86
87 _ => Err(BindingError {
88 kind: BindingErrorKind::UnknownMethod,
89 message: format!("Method '{}' not supported on {:?}", method, receiver),
90 span: crate::ir::span::Span::new(0, 0, 0, 0),
91 suggestion: None,
92 }),
93 }
94}
95
96fn evaluate_binary_op(
98 binary_expr: &BinaryOpExpr,
99 model: &dyn UiBindable,
100) -> Result<BindingValue, BindingError> {
101 let left = evaluate_expr(&binary_expr.left, model)?;
102 let right = evaluate_expr(&binary_expr.right, model)?;
103
104 match binary_expr.op {
105 BinaryOp::Add => match (left, right) {
107 (BindingValue::Integer(a), BindingValue::Integer(b)) => {
108 Ok(BindingValue::Integer(a + b))
109 }
110 (BindingValue::Float(a), BindingValue::Float(b)) => Ok(BindingValue::Float(a + b)),
111 (BindingValue::String(a), BindingValue::String(b)) => Ok(BindingValue::String(a + &b)),
112 _ => Err(BindingError {
113 kind: BindingErrorKind::InvalidOperation,
114 message: "Cannot add these types".to_string(),
115 span: crate::ir::span::Span::new(0, 0, 0, 0),
116 suggestion: None,
117 }),
118 },
119 BinaryOp::Sub => match (left, right) {
120 (BindingValue::Integer(a), BindingValue::Integer(b)) => {
121 Ok(BindingValue::Integer(a - b))
122 }
123 (BindingValue::Float(a), BindingValue::Float(b)) => Ok(BindingValue::Float(a - b)),
124 _ => Err(BindingError {
125 kind: BindingErrorKind::InvalidOperation,
126 message: "Cannot subtract these types".to_string(),
127 span: crate::ir::span::Span::new(0, 0, 0, 0),
128 suggestion: None,
129 }),
130 },
131 BinaryOp::Mul => match (left, right) {
132 (BindingValue::Integer(a), BindingValue::Integer(b)) => {
133 Ok(BindingValue::Integer(a * b))
134 }
135 (BindingValue::Float(a), BindingValue::Float(b)) => Ok(BindingValue::Float(a * b)),
136 _ => Err(BindingError {
137 kind: BindingErrorKind::InvalidOperation,
138 message: "Cannot multiply these types".to_string(),
139 span: crate::ir::span::Span::new(0, 0, 0, 0),
140 suggestion: None,
141 }),
142 },
143 BinaryOp::Div => match (left, right) {
144 (BindingValue::Integer(a), BindingValue::Integer(b)) if b != 0 => {
145 Ok(BindingValue::Integer(a / b))
146 }
147 (BindingValue::Float(a), BindingValue::Float(b)) if b != 0.0 => {
148 Ok(BindingValue::Float(a / b))
149 }
150 _ => Err(BindingError {
151 kind: BindingErrorKind::InvalidOperation,
152 message: "Cannot divide these types or division by zero".to_string(),
153 span: crate::ir::span::Span::new(0, 0, 0, 0),
154 suggestion: None,
155 }),
156 },
157
158 BinaryOp::Eq => Ok(BindingValue::Bool(left == right)),
160 BinaryOp::Ne => Ok(BindingValue::Bool(left != right)),
161 BinaryOp::Lt => Ok(BindingValue::Bool(compare_values(&left, &right, |a, b| {
162 a < b
163 }))),
164 BinaryOp::Le => Ok(BindingValue::Bool(compare_values(&left, &right, |a, b| {
165 a <= b
166 }))),
167 BinaryOp::Gt => Ok(BindingValue::Bool(compare_values(&left, &right, |a, b| {
168 a > b
169 }))),
170 BinaryOp::Ge => Ok(BindingValue::Bool(compare_values(&left, &right, |a, b| {
171 a >= b
172 }))),
173
174 BinaryOp::And => {
176 let left_bool = left.to_bool();
177 let right_bool = right.to_bool();
178 Ok(BindingValue::Bool(left_bool && right_bool))
179 }
180 BinaryOp::Or => {
181 let left_bool = left.to_bool();
182 let right_bool = right.to_bool();
183 Ok(BindingValue::Bool(left_bool || right_bool))
184 }
185 }
186}
187
188fn compare_values<F>(left: &BindingValue, right: &BindingValue, cmp: F) -> bool
190where
191 F: Fn(f64, f64) -> bool,
192{
193 match (left, right) {
194 (BindingValue::Integer(a), BindingValue::Integer(b)) => cmp(*a as f64, *b as f64),
195 (BindingValue::Float(a), BindingValue::Float(b)) => cmp(*a, *b),
196 (BindingValue::String(a), BindingValue::String(b)) => cmp(a.len() as f64, b.len() as f64),
197 (BindingValue::List(a), BindingValue::List(b)) => cmp(a.len() as f64, b.len() as f64),
198 _ => false,
199 }
200}
201
202fn evaluate_unary_op(
204 unary_expr: &UnaryOpExpr,
205 model: &dyn UiBindable,
206) -> Result<BindingValue, BindingError> {
207 let operand = evaluate_expr(&unary_expr.operand, model)?;
208
209 match unary_expr.op {
210 UnaryOp::Not => Ok(BindingValue::Bool(!operand.to_bool())),
211 UnaryOp::Neg => match operand {
212 BindingValue::Integer(i) => Ok(BindingValue::Integer(-i)),
213 BindingValue::Float(f) => Ok(BindingValue::Float(-f)),
214 _ => Err(BindingError {
215 kind: BindingErrorKind::InvalidOperation,
216 message: "Cannot negate this type".to_string(),
217 span: crate::ir::span::Span::new(0, 0, 0, 0),
218 suggestion: None,
219 }),
220 },
221 }
222}
223
224fn evaluate_conditional(
226 conditional_expr: &ConditionalExpr,
227 model: &dyn UiBindable,
228) -> Result<BindingValue, BindingError> {
229 let condition = evaluate_expr(&conditional_expr.condition, model)?;
230
231 if condition.to_bool() {
232 evaluate_expr(&conditional_expr.then_branch, model)
233 } else {
234 evaluate_expr(&conditional_expr.else_branch, model)
235 }
236}
237
238fn evaluate_literal(literal_expr: &LiteralExpr) -> BindingValue {
240 match literal_expr {
241 LiteralExpr::String(s) => BindingValue::String(s.clone()),
242 LiteralExpr::Integer(i) => BindingValue::Integer(*i),
243 LiteralExpr::Float(f) => BindingValue::Float(*f),
244 LiteralExpr::Bool(b) => BindingValue::Bool(*b),
245 }
246}
247
248pub fn evaluate_binding_expr(
250 binding_expr: &crate::expr::BindingExpr,
251 model: &dyn UiBindable,
252) -> Result<BindingValue, BindingError> {
253 match evaluate_expr(&binding_expr.expr, model) {
254 Ok(result) => Ok(result),
255 Err(mut err) => {
256 err.span = binding_expr.span;
257 Err(err)
258 }
259 }
260}
261
262pub fn evaluate_formatted(
264 parts: &[crate::ir::InterpolatedPart],
265 model: &dyn UiBindable,
266) -> Result<String, BindingError> {
267 let mut result = String::new();
268
269 for part in parts {
270 match part {
271 crate::ir::InterpolatedPart::Literal(literal) => {
272 result.push_str(literal);
273 }
274 crate::ir::InterpolatedPart::Binding(binding_expr) => {
275 let value = evaluate_binding_expr(binding_expr, model)?;
276 result.push_str(&value.to_display_string());
277 }
278 }
279 }
280
281 Ok(result)
282}
283
284pub fn evaluate_expr_with_shared(
301 expr: &Expr,
302 model: &dyn UiBindable,
303 shared: Option<&dyn UiBindable>,
304) -> Result<BindingValue, BindingError> {
305 match expr {
306 Expr::FieldAccess(field_expr) => evaluate_field_access(field_expr, model),
307 Expr::SharedFieldAccess(shared_expr) => evaluate_shared_field_access(shared_expr, shared),
308 Expr::MethodCall(method_expr) => {
309 evaluate_method_call_with_shared(method_expr, model, shared)
310 }
311 Expr::BinaryOp(binary_expr) => evaluate_binary_op_with_shared(binary_expr, model, shared),
312 Expr::UnaryOp(unary_expr) => evaluate_unary_op_with_shared(unary_expr, model, shared),
313 Expr::Conditional(conditional_expr) => {
314 evaluate_conditional_with_shared(conditional_expr, model, shared)
315 }
316 Expr::Literal(literal_expr) => Ok(evaluate_literal(literal_expr)),
317 }
318}
319
320fn evaluate_shared_field_access(
322 shared_expr: &SharedFieldAccessExpr,
323 shared: Option<&dyn UiBindable>,
324) -> Result<BindingValue, BindingError> {
325 let Some(shared_ctx) = shared else {
326 return Ok(BindingValue::String(String::new()));
328 };
329
330 let path: Vec<&str> = shared_expr.path.iter().map(|s| s.as_str()).collect();
331
332 shared_ctx.get_field(&path).ok_or_else(|| {
333 let field_name = format!("shared.{}", shared_expr.path.join("."));
334 BindingError {
335 kind: BindingErrorKind::UnknownField,
336 message: format!("Shared field '{}' not found", field_name),
337 span: crate::ir::span::Span::new(0, 0, 0, 0),
338 suggestion: None,
339 }
340 })
341}
342
343fn evaluate_method_call_with_shared(
345 method_expr: &MethodCallExpr,
346 model: &dyn UiBindable,
347 shared: Option<&dyn UiBindable>,
348) -> Result<BindingValue, BindingError> {
349 let receiver = evaluate_expr_with_shared(&method_expr.receiver, model, shared)?;
350 let method = &method_expr.method;
351
352 let _args: Vec<BindingValue> = method_expr
354 .args
355 .iter()
356 .map(|arg| evaluate_expr_with_shared(arg, model, shared))
357 .collect::<Result<Vec<_>, _>>()?;
358
359 match (receiver.clone(), method.as_str()) {
360 (BindingValue::String(s), "len") => Ok(BindingValue::Integer(s.len() as i64)),
362 (BindingValue::String(s), "to_uppercase") => Ok(BindingValue::String(s.to_uppercase())),
363 (BindingValue::String(s), "to_lowercase") => Ok(BindingValue::String(s.to_lowercase())),
364 (BindingValue::String(s), "trim") => Ok(BindingValue::String(s.trim().to_string())),
365
366 (BindingValue::List(l), "len") => Ok(BindingValue::Integer(l.len() as i64)),
368 (BindingValue::List(l), "is_empty") => Ok(BindingValue::Bool(l.is_empty())),
369
370 (BindingValue::Integer(i), "to_string") => Ok(BindingValue::String(i.to_string())),
372
373 (BindingValue::Float(f), "to_string") => Ok(BindingValue::String(f.to_string())),
375 (BindingValue::Float(f), "round") => Ok(BindingValue::Float(f.round())),
376 (BindingValue::Float(f), "floor") => Ok(BindingValue::Float(f.floor())),
377 (BindingValue::Float(f), "ceil") => Ok(BindingValue::Float(f.ceil())),
378
379 (BindingValue::Bool(b), "to_string") => Ok(BindingValue::String(b.to_string())),
381
382 _ => Err(BindingError {
383 kind: BindingErrorKind::UnknownMethod,
384 message: format!("Method '{}' not supported on {:?}", method, receiver),
385 span: crate::ir::span::Span::new(0, 0, 0, 0),
386 suggestion: None,
387 }),
388 }
389}
390
391fn evaluate_binary_op_with_shared(
393 binary_expr: &BinaryOpExpr,
394 model: &dyn UiBindable,
395 shared: Option<&dyn UiBindable>,
396) -> Result<BindingValue, BindingError> {
397 let left = evaluate_expr_with_shared(&binary_expr.left, model, shared)?;
398 let right = evaluate_expr_with_shared(&binary_expr.right, model, shared)?;
399
400 match binary_expr.op {
401 BinaryOp::Add => match (left, right) {
403 (BindingValue::Integer(a), BindingValue::Integer(b)) => {
404 Ok(BindingValue::Integer(a + b))
405 }
406 (BindingValue::Float(a), BindingValue::Float(b)) => Ok(BindingValue::Float(a + b)),
407 (BindingValue::String(a), BindingValue::String(b)) => Ok(BindingValue::String(a + &b)),
408 _ => Err(BindingError {
409 kind: BindingErrorKind::InvalidOperation,
410 message: "Cannot add these types".to_string(),
411 span: crate::ir::span::Span::new(0, 0, 0, 0),
412 suggestion: None,
413 }),
414 },
415 BinaryOp::Sub => match (left, right) {
416 (BindingValue::Integer(a), BindingValue::Integer(b)) => {
417 Ok(BindingValue::Integer(a - b))
418 }
419 (BindingValue::Float(a), BindingValue::Float(b)) => Ok(BindingValue::Float(a - b)),
420 _ => Err(BindingError {
421 kind: BindingErrorKind::InvalidOperation,
422 message: "Cannot subtract these types".to_string(),
423 span: crate::ir::span::Span::new(0, 0, 0, 0),
424 suggestion: None,
425 }),
426 },
427 BinaryOp::Mul => match (left, right) {
428 (BindingValue::Integer(a), BindingValue::Integer(b)) => {
429 Ok(BindingValue::Integer(a * b))
430 }
431 (BindingValue::Float(a), BindingValue::Float(b)) => Ok(BindingValue::Float(a * b)),
432 _ => Err(BindingError {
433 kind: BindingErrorKind::InvalidOperation,
434 message: "Cannot multiply these types".to_string(),
435 span: crate::ir::span::Span::new(0, 0, 0, 0),
436 suggestion: None,
437 }),
438 },
439 BinaryOp::Div => match (left, right) {
440 (BindingValue::Integer(a), BindingValue::Integer(b)) if b != 0 => {
441 Ok(BindingValue::Integer(a / b))
442 }
443 (BindingValue::Float(a), BindingValue::Float(b)) if b != 0.0 => {
444 Ok(BindingValue::Float(a / b))
445 }
446 _ => Err(BindingError {
447 kind: BindingErrorKind::InvalidOperation,
448 message: "Cannot divide these types or division by zero".to_string(),
449 span: crate::ir::span::Span::new(0, 0, 0, 0),
450 suggestion: None,
451 }),
452 },
453
454 BinaryOp::Eq => Ok(BindingValue::Bool(left == right)),
456 BinaryOp::Ne => Ok(BindingValue::Bool(left != right)),
457 BinaryOp::Lt => Ok(BindingValue::Bool(compare_values(&left, &right, |a, b| {
458 a < b
459 }))),
460 BinaryOp::Le => Ok(BindingValue::Bool(compare_values(&left, &right, |a, b| {
461 a <= b
462 }))),
463 BinaryOp::Gt => Ok(BindingValue::Bool(compare_values(&left, &right, |a, b| {
464 a > b
465 }))),
466 BinaryOp::Ge => Ok(BindingValue::Bool(compare_values(&left, &right, |a, b| {
467 a >= b
468 }))),
469
470 BinaryOp::And => {
472 let left_bool = left.to_bool();
473 let right_bool = right.to_bool();
474 Ok(BindingValue::Bool(left_bool && right_bool))
475 }
476 BinaryOp::Or => {
477 let left_bool = left.to_bool();
478 let right_bool = right.to_bool();
479 Ok(BindingValue::Bool(left_bool || right_bool))
480 }
481 }
482}
483
484fn evaluate_unary_op_with_shared(
486 unary_expr: &UnaryOpExpr,
487 model: &dyn UiBindable,
488 shared: Option<&dyn UiBindable>,
489) -> Result<BindingValue, BindingError> {
490 let operand = evaluate_expr_with_shared(&unary_expr.operand, model, shared)?;
491
492 match unary_expr.op {
493 UnaryOp::Not => Ok(BindingValue::Bool(!operand.to_bool())),
494 UnaryOp::Neg => match operand {
495 BindingValue::Integer(i) => Ok(BindingValue::Integer(-i)),
496 BindingValue::Float(f) => Ok(BindingValue::Float(-f)),
497 _ => Err(BindingError {
498 kind: BindingErrorKind::InvalidOperation,
499 message: "Cannot negate this type".to_string(),
500 span: crate::ir::span::Span::new(0, 0, 0, 0),
501 suggestion: None,
502 }),
503 },
504 }
505}
506
507fn evaluate_conditional_with_shared(
509 conditional_expr: &ConditionalExpr,
510 model: &dyn UiBindable,
511 shared: Option<&dyn UiBindable>,
512) -> Result<BindingValue, BindingError> {
513 let condition = evaluate_expr_with_shared(&conditional_expr.condition, model, shared)?;
514
515 if condition.to_bool() {
516 evaluate_expr_with_shared(&conditional_expr.then_branch, model, shared)
517 } else {
518 evaluate_expr_with_shared(&conditional_expr.else_branch, model, shared)
519 }
520}
521
522pub fn evaluate_binding_expr_with_shared(
524 binding_expr: &crate::expr::BindingExpr,
525 model: &dyn UiBindable,
526 shared: Option<&dyn UiBindable>,
527) -> Result<BindingValue, BindingError> {
528 match evaluate_expr_with_shared(&binding_expr.expr, model, shared) {
529 Ok(result) => Ok(result),
530 Err(mut err) => {
531 err.span = binding_expr.span;
532 Err(err)
533 }
534 }
535}
536
537pub fn evaluate_formatted_with_shared(
539 parts: &[crate::ir::InterpolatedPart],
540 model: &dyn UiBindable,
541 shared: Option<&dyn UiBindable>,
542) -> Result<String, BindingError> {
543 let mut result = String::new();
544
545 for part in parts {
546 match part {
547 crate::ir::InterpolatedPart::Literal(literal) => {
548 result.push_str(literal);
549 }
550 crate::ir::InterpolatedPart::Binding(binding_expr) => {
551 let value = evaluate_binding_expr_with_shared(binding_expr, model, shared)?;
552 result.push_str(&value.to_display_string());
553 }
554 }
555 }
556
557 Ok(result)
558}