1use crate::{
2 error::Error,
3 prelude::{Scalar, Tuple},
4 types::AttributeName,
5};
6
7#[derive(Debug)]
21pub enum Expression {
22 Attribute(AttributeName),
24 Const(Scalar),
26}
27
28impl Expression {
29 pub fn eval(&self, tuple: &Tuple) -> Result<Scalar, Error> {
52 match self {
53 Expression::Attribute(attr) => tuple
54 .get(attr)
55 .cloned()
56 .ok_or(Error::AttributeNotFound { name: attr.clone() }),
57 Expression::Const(val) => Ok(val.clone()),
58 }
59 }
60}
61
62impl From<AttributeName> for Expression {
63 fn from(attr: AttributeName) -> Self {
64 Expression::Attribute(attr)
65 }
66}
67
68impl From<Scalar> for Expression {
69 fn from(val: Scalar) -> Self {
70 Expression::Const(val)
71 }
72}
73
74#[derive(Debug)]
86pub enum Predicate {
87 Not(Box<Predicate>),
89 And(Box<Predicate>, Box<Predicate>),
93 Or(Box<Predicate>, Box<Predicate>),
97 Eq(Expression, Expression),
103 Gt(Expression, Expression),
108 Lt(Expression, Expression),
113}
114
115impl Predicate {
116 pub fn not<P>(predicate: P) -> Self
137 where
138 P: Into<Predicate>,
139 {
140 Self::Not(Box::new(predicate.into()))
141 }
142
143 pub fn and<L, R>(lhs: L, rhs: R) -> Self
163 where
164 L: Into<Predicate>,
165 R: Into<Predicate>,
166 {
167 Self::And(Box::new(lhs.into()), Box::new(rhs.into()))
168 }
169
170 pub fn or<L, R>(lhs: L, rhs: R) -> Self
190 where
191 L: Into<Predicate>,
192 R: Into<Predicate>,
193 {
194 Self::Or(Box::new(lhs.into()), Box::new(rhs.into()))
195 }
196
197 pub fn eq<L, R>(lhs: L, rhs: R) -> Self
226 where
227 L: Into<Expression>,
228 R: Into<Expression>,
229 {
230 Predicate::Eq(lhs.into(), rhs.into())
231 }
232
233 pub fn gt<L, R>(lhs: L, rhs: R) -> Self
251 where
252 L: Into<Expression>,
253 R: Into<Expression>,
254 {
255 Predicate::Gt(lhs.into(), rhs.into())
256 }
257
258 pub fn lt<L, R>(lhs: L, rhs: R) -> Self
276 where
277 L: Into<Expression>,
278 R: Into<Expression>,
279 {
280 Predicate::Lt(lhs.into(), rhs.into())
281 }
282
283 pub fn eval(&self, tuple: &Tuple) -> Result<bool, Error> {
309 match self {
310 Predicate::Not(expr) => {
311 let expr = expr.eval(tuple)?;
312 Ok(!expr)
313 }
314 Predicate::And(lhs, rhs) => {
315 let lhs = lhs.eval(tuple)?;
316 let rhs = rhs.eval(tuple)?;
317 Ok(lhs && rhs)
318 }
319 Predicate::Or(lhs, rhs) => {
320 let lhs = lhs.eval(tuple)?;
321 let rhs = rhs.eval(tuple)?;
322 Ok(lhs || rhs)
323 }
324 Predicate::Eq(lhs, rhs) => {
325 let lhs = lhs.eval(tuple)?;
326 let rhs = rhs.eval(tuple)?;
327 if lhs.ty() != rhs.ty() {
328 return Err(Error::ScalarTypeMismatch {
329 lhs: lhs.ty(),
330 rhs: rhs.ty(),
331 });
332 }
333 Ok(lhs == rhs)
334 }
335 Predicate::Gt(lhs, rhs) => {
336 let lhs = lhs.eval(tuple)?;
337 let rhs = rhs.eval(tuple)?;
338 if !matches!(lhs, Scalar::Integer(_)) || !matches!(rhs, Scalar::Integer(_)) {
339 return Err(Error::NonComparableTypes {
340 lhs: lhs.ty(),
341 rhs: rhs.ty(),
342 });
343 }
344 Ok(lhs > rhs)
345 }
346 Predicate::Lt(lhs, rhs) => {
347 let lhs = lhs.eval(tuple)?;
348 let rhs = rhs.eval(tuple)?;
349 if !matches!(lhs, Scalar::Integer(_)) || !matches!(rhs, Scalar::Integer(_)) {
350 return Err(Error::NonComparableTypes {
351 lhs: lhs.ty(),
352 rhs: rhs.ty(),
353 });
354 }
355 Ok(lhs < rhs)
356 }
357 }
358 }
359}
360
361#[cfg(test)]
362mod tests {
363 use super::*;
364
365 fn integer_tuple(name: &str, value: i64) -> Tuple {
366 Tuple::try_from(vec![(AttributeName::from(name), Scalar::Integer(value))]).unwrap()
367 }
368
369 fn boolean_tuple(name: &str, value: bool) -> Tuple {
370 Tuple::try_from(vec![(AttributeName::from(name), Scalar::Boolean(value))]).unwrap()
371 }
372
373 fn binary_tuple(name: &str, value: Vec<u8>) -> Tuple {
374 Tuple::try_from(vec![(AttributeName::from(name), Scalar::Binary(value))]).unwrap()
375 }
376
377 fn string_tuple(name: &str, value: &str) -> Tuple {
378 Tuple::try_from(vec![(
379 AttributeName::from(name),
380 Scalar::String(value.to_string()),
381 )])
382 .unwrap()
383 }
384
385 #[test]
386 fn test_expression_from_attribute_name() {
387 let expression = Expression::from(AttributeName::from("age"));
388
389 match expression {
390 Expression::Attribute(attribute) => assert_eq!(attribute, AttributeName::from("age")),
391 Expression::Const(_) => panic!("expected attribute expression"),
392 }
393 }
394
395 #[test]
396 fn test_expression_from_scalar() {
397 let expression = Expression::from(Scalar::Integer(42));
398
399 match expression {
400 Expression::Const(value) => assert_eq!(value, Scalar::Integer(42)),
401 Expression::Attribute(_) => panic!("expected const expression"),
402 }
403 }
404
405 #[test]
406 fn test_and() {
407 let tuple = integer_tuple("foo", 42);
408 let predicate = Predicate::and(
409 Predicate::eq(AttributeName::from("foo"), Scalar::Integer(42)),
410 Predicate::eq(AttributeName::from("foo"), Scalar::Integer(42)),
411 );
412 assert!(predicate.eval(&tuple).unwrap());
413 }
414
415 #[test]
416 fn test_predicate_eq_helper() {
417 let predicate = Predicate::eq(AttributeName::from("age"), Scalar::Integer(42));
418
419 match predicate {
420 Predicate::Eq(Expression::Attribute(attribute), Expression::Const(value)) => {
421 assert_eq!(attribute, AttributeName::from("age"));
422 assert_eq!(value, Scalar::Integer(42));
423 }
424 _ => panic!("expected eq predicate"),
425 }
426 }
427
428 #[test]
429 fn test_eq() {
430 let tuple = integer_tuple("foo", 42);
431 let predicate = Predicate::eq(AttributeName::from("foo"), Scalar::Integer(42));
432 assert!(predicate.eval(&tuple).unwrap());
433 }
434
435 #[test]
436 fn test_eq_supports_boolean_operands() {
437 let tuple = boolean_tuple("active", true);
438 let predicate = Predicate::eq(AttributeName::from("active"), Scalar::Boolean(true));
439
440 assert!(predicate.eval(&tuple).unwrap());
441 }
442
443 #[test]
444 fn test_eq_supports_string_operands() {
445 let tuple = string_tuple("city", "Berlin");
446 let predicate = Predicate::eq(AttributeName::from("city"), Scalar::String("Berlin".into()));
447
448 assert!(predicate.eval(&tuple).unwrap());
449 }
450
451 #[test]
452 fn test_eq_supports_binary_operands() {
453 let tuple = binary_tuple("payload", vec![1, 2, 3]);
454 let predicate = Predicate::eq(
455 AttributeName::from("payload"),
456 Scalar::Binary(vec![1, 2, 3]),
457 );
458
459 assert!(predicate.eval(&tuple).unwrap());
460 }
461
462 #[test]
463 fn test_predicate_gt_helper() {
464 let predicate = Predicate::gt(AttributeName::from("age"), Scalar::Integer(18));
465
466 match predicate {
467 Predicate::Gt(Expression::Attribute(attribute), Expression::Const(value)) => {
468 assert_eq!(attribute, AttributeName::from("age"));
469 assert_eq!(value, Scalar::Integer(18));
470 }
471 _ => panic!("expected gt predicate"),
472 }
473 }
474
475 #[test]
476 fn test_not() {
477 let tuple = integer_tuple("foo", 42);
478 let predicate = Predicate::not(Predicate::eq(
479 AttributeName::from("foo"),
480 Scalar::Integer(42),
481 ));
482
483 assert!(!predicate.eval(&tuple).unwrap());
484 }
485
486 #[test]
487 fn test_predicate_lt_helper() {
488 let predicate = Predicate::lt(AttributeName::from("age"), Scalar::Integer(30));
489
490 match predicate {
491 Predicate::Lt(Expression::Attribute(attribute), Expression::Const(value)) => {
492 assert_eq!(attribute, AttributeName::from("age"));
493 assert_eq!(value, Scalar::Integer(30));
494 }
495 _ => panic!("expected lt predicate"),
496 }
497 }
498
499 #[test]
500 fn test_or() {
501 let tuple = integer_tuple("foo", 42);
502 let predicate = Predicate::or(
503 Predicate::eq(AttributeName::from("foo"), Scalar::Integer(0)),
504 Predicate::eq(AttributeName::from("foo"), Scalar::Integer(42)),
505 );
506
507 assert!(predicate.eval(&tuple).unwrap());
508 }
509
510 #[test]
511 fn test_expression_const_returns_value() -> Result<(), Error> {
512 let tuple = Tuple::empty();
513
514 assert_eq!(
515 Expression::Const(Scalar::Boolean(true)).eval(&tuple)?,
516 Scalar::Boolean(true)
517 );
518 Ok(())
519 }
520
521 #[test]
522 fn test_expression_attribute_missing_is_error() {
523 let tuple = Tuple::empty();
524
525 assert_eq!(
526 Expression::Attribute(AttributeName::from("missing")).eval(&tuple),
527 Err(Error::AttributeNotFound {
528 name: AttributeName::from("missing")
529 })
530 );
531 }
532
533 #[test]
534 fn test_and_propagates_attribute_errors() {
535 let tuple = integer_tuple("foo", 42);
536 let predicate = Predicate::and(
537 Predicate::eq(AttributeName::from("foo"), Scalar::Integer(42)),
538 Predicate::eq(AttributeName::from("missing"), Scalar::Integer(42)),
539 );
540
541 assert_eq!(
542 predicate.eval(&tuple),
543 Err(Error::AttributeNotFound {
544 name: AttributeName::from("missing")
545 })
546 );
547 }
548
549 #[test]
550 fn test_and_does_not_hide_missing_attribute_when_lhs_is_false() {
551 let tuple = integer_tuple("foo", 42);
552 let predicate = Predicate::and(
553 Predicate::eq(AttributeName::from("foo"), Scalar::Integer(0)),
554 Predicate::eq(AttributeName::from("missing"), Scalar::Integer(42)),
555 );
556
557 assert_eq!(
558 predicate.eval(&tuple),
559 Err(Error::AttributeNotFound {
560 name: AttributeName::from("missing")
561 })
562 );
563 }
564
565 #[test]
566 fn test_or_propagates_attribute_errors() {
567 let tuple = integer_tuple("foo", 42);
568 let predicate = Predicate::or(
569 Predicate::eq(AttributeName::from("foo"), Scalar::Integer(42)),
570 Predicate::eq(AttributeName::from("missing"), Scalar::Integer(42)),
571 );
572
573 assert_eq!(
574 predicate.eval(&tuple),
575 Err(Error::AttributeNotFound {
576 name: AttributeName::from("missing")
577 })
578 );
579 }
580
581 #[test]
582 fn test_gt() {
583 let tuple = integer_tuple("age", 42);
584 let predicate = Predicate::gt(AttributeName::from("age"), Scalar::Integer(20));
585
586 assert!(predicate.eval(&tuple).unwrap());
587 }
588
589 #[test]
590 fn test_gt_rejects_heterogeneous_scalar_types() {
591 let tuple = integer_tuple("age", 42);
592 let predicate = Predicate::gt(AttributeName::from("age"), Scalar::String("20".into()));
593
594 assert!(
595 predicate.eval(&tuple).is_err(),
596 "greater-than comparison between different scalar types must return an error"
597 );
598 }
599
600 #[test]
601 fn test_gt_rejects_string_comparisons() {
602 let tuple = string_tuple("city", "Berlin");
603 let predicate = Predicate::gt(
604 AttributeName::from("city"),
605 Scalar::String("Amsterdam".into()),
606 );
607
608 assert!(
609 predicate.eval(&tuple).is_err(),
610 "greater-than comparison for string operands must return an error"
611 );
612 }
613
614 #[test]
615 fn test_gt_rejects_boolean_comparisons() {
616 let tuple = boolean_tuple("active", true);
617 let predicate = Predicate::gt(AttributeName::from("active"), Scalar::Boolean(false));
618
619 assert!(
620 predicate.eval(&tuple).is_err(),
621 "greater-than comparison for boolean operands must return an error"
622 );
623 }
624
625 #[test]
626 fn test_gt_rejects_binary_comparisons() {
627 let tuple = binary_tuple("payload", vec![1, 2, 3]);
628 let predicate = Predicate::gt(
629 AttributeName::from("payload"),
630 Scalar::Binary(vec![0, 1, 2]),
631 );
632
633 assert!(
634 predicate.eval(&tuple).is_err(),
635 "greater-than comparison for binary operands must return an error"
636 );
637 }
638
639 #[test]
640 fn test_lt() {
641 let tuple = integer_tuple("age", 18);
642 let predicate = Predicate::lt(AttributeName::from("age"), Scalar::Integer(20));
643
644 assert!(predicate.eval(&tuple).unwrap());
645 }
646
647 #[test]
648 fn test_eq_rejects_heterogeneous_scalar_types() {
649 let tuple = integer_tuple("age", 42);
650 let predicate = Predicate::eq(AttributeName::from("age"), Scalar::String("42".into()));
651
652 assert!(
653 predicate.eval(&tuple).is_err(),
654 "equality comparison between different scalar types must return an error"
655 );
656 }
657
658 #[test]
659 fn test_lt_rejects_heterogeneous_scalar_types() {
660 let tuple = integer_tuple("age", 42);
661 let predicate = Predicate::lt(AttributeName::from("age"), Scalar::String("20".into()));
662
663 assert!(
664 predicate.eval(&tuple).is_err(),
665 "less-than comparison between different scalar types must return an error"
666 );
667 }
668
669 #[test]
670 fn test_lt_rejects_string_comparisons() {
671 let tuple = string_tuple("city", "Berlin");
672 let predicate = Predicate::lt(AttributeName::from("city"), Scalar::String("Paris".into()));
673
674 assert!(
675 predicate.eval(&tuple).is_err(),
676 "less-than comparison for string operands must return an error"
677 );
678 }
679
680 #[test]
681 fn test_lt_rejects_boolean_comparisons() {
682 let tuple = boolean_tuple("active", true);
683 let predicate = Predicate::lt(AttributeName::from("active"), Scalar::Boolean(false));
684
685 assert!(
686 predicate.eval(&tuple).is_err(),
687 "less-than comparison for boolean operands must return an error"
688 );
689 }
690
691 #[test]
692 fn test_lt_rejects_binary_comparisons() {
693 let tuple = binary_tuple("payload", vec![1, 2, 3]);
694 let predicate = Predicate::lt(
695 AttributeName::from("payload"),
696 Scalar::Binary(vec![4, 5, 6]),
697 );
698
699 assert!(
700 predicate.eval(&tuple).is_err(),
701 "less-than comparison for binary operands must return an error"
702 );
703 }
704}