1use super::binding::{Binding, Value, Var};
30use std::fmt;
31
32#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct Triple {
35 pub subject: Pattern,
37 pub predicate: Pattern,
39 pub object: Pattern,
41}
42
43impl Triple {
44 pub fn new(subject: Pattern, predicate: Pattern, object: Pattern) -> Self {
46 Self {
47 subject,
48 predicate,
49 object,
50 }
51 }
52
53 pub fn vars(&self) -> Vec<Var> {
55 let mut vars = Vec::new();
56 if let Pattern::Var(v) = &self.subject {
57 vars.push(v.clone());
58 }
59 if let Pattern::Var(v) = &self.predicate {
60 vars.push(v.clone());
61 }
62 if let Pattern::Var(v) = &self.object {
63 vars.push(v.clone());
64 }
65 vars
66 }
67
68 pub fn is_concrete(&self) -> bool {
70 !matches!(self.subject, Pattern::Var(_))
71 && !matches!(self.predicate, Pattern::Var(_))
72 && !matches!(self.object, Pattern::Var(_))
73 }
74}
75
76impl fmt::Display for Triple {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 write!(f, "({} {} {})", self.subject, self.predicate, self.object)
79 }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
84pub enum Pattern {
85 Var(Var),
87 Uri(String),
89 Literal(String),
91 TypedLiteral(String, String),
93 Any,
95}
96
97impl Pattern {
98 pub fn is_var(&self) -> bool {
100 matches!(self, Pattern::Var(_))
101 }
102
103 pub fn as_var(&self) -> Option<&Var> {
105 match self {
106 Pattern::Var(v) => Some(v),
107 _ => None,
108 }
109 }
110
111 pub fn to_value(&self) -> Option<Value> {
113 match self {
114 Pattern::Var(_) => None,
115 Pattern::Uri(s) => Some(Value::Uri(s.clone())),
116 Pattern::Literal(s) => Some(Value::String(s.clone())),
117 Pattern::TypedLiteral(v, _) => Some(Value::String(v.clone())),
118 Pattern::Any => None,
119 }
120 }
121}
122
123impl fmt::Display for Pattern {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 match self {
126 Pattern::Var(v) => write!(f, "{}", v),
127 Pattern::Uri(s) => write!(f, "<{}>", s),
128 Pattern::Literal(s) => write!(f, "\"{}\"", s),
129 Pattern::TypedLiteral(v, t) => write!(f, "\"{}\"^^<{}>", v, t),
130 Pattern::Any => write!(f, "_"),
131 }
132 }
133}
134
135#[derive(Debug, Clone)]
137pub enum Op {
138 BGP(OpBGP),
140 Triple(OpTriple),
142 Join(OpJoin),
144 LeftJoin(OpLeftJoin),
146 RightJoin(OpRightJoin),
148 CrossJoin(OpCrossJoin),
150 Filter(OpFilter),
152 Union(OpUnion),
154 Project(OpProject),
156 Distinct(OpDistinct),
158 Reduced(OpReduced),
160 Slice(OpSlice),
162 Order(OpOrder),
164 Group(OpGroup),
166 Extend(OpExtend),
168 Minus(OpMinus),
170 Intersect(OpIntersect),
172 Table(OpTable),
174 Sequence(OpSequence),
176 Disjunction(OpDisjunction),
178 Null(OpNull),
180}
181
182impl Op {
183 pub fn vars(&self) -> Vec<Var> {
185 match self {
186 Op::BGP(op) => op.vars(),
187 Op::Triple(op) => op.triple.vars(),
188 Op::Join(op) => {
189 let mut vars = op.left.vars();
190 for v in op.right.vars() {
191 if !vars.contains(&v) {
192 vars.push(v);
193 }
194 }
195 vars
196 }
197 Op::LeftJoin(op) => {
198 let mut vars = op.left.vars();
199 for v in op.right.vars() {
200 if !vars.contains(&v) {
201 vars.push(v);
202 }
203 }
204 vars
205 }
206 Op::RightJoin(op) => {
207 let mut vars = op.left.vars();
208 for v in op.right.vars() {
209 if !vars.contains(&v) {
210 vars.push(v);
211 }
212 }
213 vars
214 }
215 Op::CrossJoin(op) => {
216 let mut vars = op.left.vars();
217 for v in op.right.vars() {
218 if !vars.contains(&v) {
219 vars.push(v);
220 }
221 }
222 vars
223 }
224 Op::Filter(op) => op.sub_op.vars(),
225 Op::Union(op) => {
226 let mut vars = op.left.vars();
227 for v in op.right.vars() {
228 if !vars.contains(&v) {
229 vars.push(v);
230 }
231 }
232 vars
233 }
234 Op::Project(op) => op.vars.clone(),
235 Op::Distinct(op) => op.sub_op.vars(),
236 Op::Reduced(op) => op.sub_op.vars(),
237 Op::Slice(op) => op.sub_op.vars(),
238 Op::Order(op) => op.sub_op.vars(),
239 Op::Group(op) => {
240 let mut vars = op.group_vars.clone();
241 for (v, _) in &op.aggregates {
242 vars.push(v.clone());
243 }
244 vars
245 }
246 Op::Extend(op) => {
247 let mut vars = op.sub_op.vars();
248 if !vars.contains(&op.var) {
249 vars.push(op.var.clone());
250 }
251 vars
252 }
253 Op::Minus(op) => op.left.vars(),
254 Op::Intersect(op) => {
255 let left_vars = op.left.vars();
257 let right_vars = op.right.vars();
258 left_vars
259 .into_iter()
260 .filter(|v| right_vars.contains(v))
261 .collect()
262 }
263 Op::Table(op) => op.vars.clone(),
264 Op::Sequence(op) => {
265 let mut vars = Vec::new();
266 for sub in &op.ops {
267 for v in sub.vars() {
268 if !vars.contains(&v) {
269 vars.push(v);
270 }
271 }
272 }
273 vars
274 }
275 Op::Disjunction(op) => {
276 let mut vars = Vec::new();
277 for sub in &op.ops {
278 for v in sub.vars() {
279 if !vars.contains(&v) {
280 vars.push(v);
281 }
282 }
283 }
284 vars
285 }
286 Op::Null(_) => Vec::new(),
287 }
288 }
289
290 pub fn is_null(&self) -> bool {
292 matches!(self, Op::Null(_))
293 }
294}
295
296#[derive(Debug, Clone)]
298pub struct OpBGP {
299 pub triples: Vec<Triple>,
301}
302
303impl OpBGP {
304 pub fn new() -> Self {
306 Self {
307 triples: Vec::new(),
308 }
309 }
310
311 pub fn from_triples(triples: Vec<Triple>) -> Self {
313 Self { triples }
314 }
315
316 pub fn add(&mut self, triple: Triple) {
318 self.triples.push(triple);
319 }
320
321 pub fn vars(&self) -> Vec<Var> {
323 let mut vars = Vec::new();
324 for triple in &self.triples {
325 for v in triple.vars() {
326 if !vars.contains(&v) {
327 vars.push(v);
328 }
329 }
330 }
331 vars
332 }
333
334 pub fn is_empty(&self) -> bool {
336 self.triples.is_empty()
337 }
338}
339
340impl Default for OpBGP {
341 fn default() -> Self {
342 Self::new()
343 }
344}
345
346#[derive(Debug, Clone)]
348pub struct OpTriple {
349 pub triple: Triple,
350}
351
352impl OpTriple {
353 pub fn new(triple: Triple) -> Self {
355 Self { triple }
356 }
357}
358
359#[derive(Debug, Clone)]
361pub struct OpJoin {
362 pub left: Box<Op>,
363 pub right: Box<Op>,
364}
365
366impl OpJoin {
367 pub fn new(left: Op, right: Op) -> Self {
369 Self {
370 left: Box::new(left),
371 right: Box::new(right),
372 }
373 }
374
375 pub fn join_all(ops: Vec<Op>) -> Op {
377 if ops.is_empty() {
378 return Op::Null(OpNull);
379 }
380
381 let mut result = ops.into_iter();
382 let mut current = result.next().unwrap();
383
384 for op in result {
385 current = Op::Join(OpJoin::new(current, op));
386 }
387
388 current
389 }
390}
391
392#[derive(Debug, Clone)]
394pub struct OpLeftJoin {
395 pub left: Box<Op>,
396 pub right: Box<Op>,
397 pub filter: Option<FilterExpr>,
399}
400
401impl OpLeftJoin {
402 pub fn new(left: Op, right: Op) -> Self {
404 Self {
405 left: Box::new(left),
406 right: Box::new(right),
407 filter: None,
408 }
409 }
410
411 pub fn with_filter(left: Op, right: Op, filter: FilterExpr) -> Self {
413 Self {
414 left: Box::new(left),
415 right: Box::new(right),
416 filter: Some(filter),
417 }
418 }
419}
420
421#[derive(Debug, Clone)]
423pub struct OpRightJoin {
424 pub left: Box<Op>,
425 pub right: Box<Op>,
426 pub filter: Option<FilterExpr>,
428}
429
430impl OpRightJoin {
431 pub fn new(left: Op, right: Op) -> Self {
433 Self {
434 left: Box::new(left),
435 right: Box::new(right),
436 filter: None,
437 }
438 }
439
440 pub fn with_filter(left: Op, right: Op, filter: FilterExpr) -> Self {
442 Self {
443 left: Box::new(left),
444 right: Box::new(right),
445 filter: Some(filter),
446 }
447 }
448}
449
450#[derive(Debug, Clone)]
452pub struct OpCrossJoin {
453 pub left: Box<Op>,
454 pub right: Box<Op>,
455}
456
457impl OpCrossJoin {
458 pub fn new(left: Op, right: Op) -> Self {
460 Self {
461 left: Box::new(left),
462 right: Box::new(right),
463 }
464 }
465}
466
467#[derive(Debug, Clone)]
469pub enum FilterExpr {
470 Eq(ExprTerm, ExprTerm),
472 NotEq(ExprTerm, ExprTerm),
474 Lt(ExprTerm, ExprTerm),
476 LtEq(ExprTerm, ExprTerm),
478 Gt(ExprTerm, ExprTerm),
480 GtEq(ExprTerm, ExprTerm),
482 And(Box<FilterExpr>, Box<FilterExpr>),
484 Or(Box<FilterExpr>, Box<FilterExpr>),
486 Not(Box<FilterExpr>),
488 Bound(Var),
490 Regex(ExprTerm, String, Option<String>),
492 StartsWith(ExprTerm, String),
494 EndsWith(ExprTerm, String),
496 Contains(ExprTerm, String),
498 IsUri(ExprTerm),
500 IsLiteral(ExprTerm),
502 IsBlank(ExprTerm),
504 In(ExprTerm, Vec<ExprTerm>),
506 NotIn(ExprTerm, Vec<ExprTerm>),
508 True,
510 False,
512}
513
514impl FilterExpr {
515 pub fn and(left: FilterExpr, right: FilterExpr) -> FilterExpr {
517 FilterExpr::And(Box::new(left), Box::new(right))
518 }
519
520 pub fn or(left: FilterExpr, right: FilterExpr) -> FilterExpr {
522 FilterExpr::Or(Box::new(left), Box::new(right))
523 }
524
525 pub fn not(expr: FilterExpr) -> FilterExpr {
527 FilterExpr::Not(Box::new(expr))
528 }
529
530 pub fn evaluate(&self, binding: &Binding) -> bool {
532 match self {
533 FilterExpr::Eq(left, right) => {
534 let l = left.evaluate(binding);
535 let r = right.evaluate(binding);
536 l == r
537 }
538 FilterExpr::NotEq(left, right) => {
539 let l = left.evaluate(binding);
540 let r = right.evaluate(binding);
541 l != r
542 }
543 FilterExpr::Lt(left, right) => compare_terms(left, right, binding, |a, b| a < b),
544 FilterExpr::LtEq(left, right) => compare_terms(left, right, binding, |a, b| a <= b),
545 FilterExpr::Gt(left, right) => compare_terms(left, right, binding, |a, b| a > b),
546 FilterExpr::GtEq(left, right) => compare_terms(left, right, binding, |a, b| a >= b),
547 FilterExpr::And(left, right) => left.evaluate(binding) && right.evaluate(binding),
548 FilterExpr::Or(left, right) => left.evaluate(binding) || right.evaluate(binding),
549 FilterExpr::Not(expr) => !expr.evaluate(binding),
550 FilterExpr::Bound(var) => binding.contains(var),
551 FilterExpr::Regex(term, pattern, flags) => {
552 if let Some(Value::String(s)) = term.evaluate(binding) {
553 let case_insensitive = flags.as_ref().map(|f| f.contains('i')).unwrap_or(false);
555 if case_insensitive {
556 s.to_lowercase().contains(&pattern.to_lowercase())
557 } else {
558 s.contains(pattern)
559 }
560 } else {
561 false
562 }
563 }
564 FilterExpr::StartsWith(term, prefix) => {
565 if let Some(Value::String(s)) = term.evaluate(binding) {
566 s.starts_with(prefix)
567 } else {
568 false
569 }
570 }
571 FilterExpr::EndsWith(term, suffix) => {
572 if let Some(Value::String(s)) = term.evaluate(binding) {
573 s.ends_with(suffix)
574 } else {
575 false
576 }
577 }
578 FilterExpr::Contains(term, substring) => {
579 if let Some(Value::String(s)) = term.evaluate(binding) {
580 s.contains(substring)
581 } else {
582 false
583 }
584 }
585 FilterExpr::IsUri(term) => {
586 matches!(term.evaluate(binding), Some(Value::Uri(_)))
587 }
588 FilterExpr::IsLiteral(term) => {
589 matches!(
590 term.evaluate(binding),
591 Some(
592 Value::String(_) | Value::Integer(_) | Value::Float(_) | Value::Boolean(_)
593 )
594 )
595 }
596 FilterExpr::IsBlank(term) => {
597 if let Some(Value::Node(id)) = term.evaluate(binding) {
598 id.starts_with("_:")
599 } else {
600 false
601 }
602 }
603 FilterExpr::In(term, list) => {
604 if let Some(val) = term.evaluate(binding) {
605 list.iter()
606 .any(|t| t.evaluate(binding) == Some(val.clone()))
607 } else {
608 false
609 }
610 }
611 FilterExpr::NotIn(term, list) => {
612 if let Some(val) = term.evaluate(binding) {
613 !list
614 .iter()
615 .any(|t| t.evaluate(binding) == Some(val.clone()))
616 } else {
617 true
618 }
619 }
620 FilterExpr::True => true,
621 FilterExpr::False => false,
622 }
623 }
624}
625
626fn compare_terms<F>(left: &ExprTerm, right: &ExprTerm, binding: &Binding, cmp: F) -> bool
628where
629 F: Fn(i64, i64) -> bool,
630{
631 match (left.evaluate(binding), right.evaluate(binding)) {
632 (Some(Value::Integer(a)), Some(Value::Integer(b))) => cmp(a, b),
633 (Some(Value::Float(a)), Some(Value::Float(b))) => cmp(a as i64, b as i64),
634 (Some(Value::Integer(a)), Some(Value::Float(b))) => cmp(a, b as i64),
635 (Some(Value::Float(a)), Some(Value::Integer(b))) => cmp(a as i64, b),
636 _ => false,
637 }
638}
639
640#[derive(Debug, Clone, PartialEq)]
642pub enum ExprTerm {
643 Var(Var),
645 Const(Value),
647 Str(Box<ExprTerm>),
649 LCase(Box<ExprTerm>),
651 UCase(Box<ExprTerm>),
653 StrLen(Box<ExprTerm>),
655 Concat(Vec<ExprTerm>),
657}
658
659impl ExprTerm {
660 pub fn evaluate(&self, binding: &Binding) -> Option<Value> {
662 match self {
663 ExprTerm::Var(var) => binding.get(var).cloned(),
664 ExprTerm::Const(val) => Some(val.clone()),
665 ExprTerm::Str(inner) => inner
666 .evaluate(binding)
667 .map(|v| Value::String(format!("{}", v))),
668 ExprTerm::LCase(inner) => {
669 if let Some(Value::String(s)) = inner.evaluate(binding) {
670 Some(Value::String(s.to_lowercase()))
671 } else {
672 None
673 }
674 }
675 ExprTerm::UCase(inner) => {
676 if let Some(Value::String(s)) = inner.evaluate(binding) {
677 Some(Value::String(s.to_uppercase()))
678 } else {
679 None
680 }
681 }
682 ExprTerm::StrLen(inner) => {
683 if let Some(Value::String(s)) = inner.evaluate(binding) {
684 Some(Value::Integer(s.len() as i64))
685 } else {
686 None
687 }
688 }
689 ExprTerm::Concat(terms) => {
690 let mut result = String::new();
691 for term in terms {
692 if let Some(Value::String(s)) = term.evaluate(binding) {
693 result.push_str(&s);
694 } else if let Some(v) = term.evaluate(binding) {
695 result.push_str(&format!("{}", v));
696 }
697 }
698 Some(Value::String(result))
699 }
700 }
701 }
702}
703
704#[derive(Debug, Clone)]
706pub struct OpFilter {
707 pub filter: FilterExpr,
708 pub sub_op: Box<Op>,
709}
710
711impl OpFilter {
712 pub fn new(filter: FilterExpr, sub_op: Op) -> Self {
714 Self {
715 filter,
716 sub_op: Box::new(sub_op),
717 }
718 }
719}
720
721#[derive(Debug, Clone)]
723pub struct OpUnion {
724 pub left: Box<Op>,
725 pub right: Box<Op>,
726}
727
728impl OpUnion {
729 pub fn new(left: Op, right: Op) -> Self {
731 Self {
732 left: Box::new(left),
733 right: Box::new(right),
734 }
735 }
736}
737
738#[derive(Debug, Clone)]
740pub struct OpProject {
741 pub vars: Vec<Var>,
742 pub sub_op: Box<Op>,
743}
744
745impl OpProject {
746 pub fn new(vars: Vec<Var>, sub_op: Op) -> Self {
748 Self {
749 vars,
750 sub_op: Box::new(sub_op),
751 }
752 }
753}
754
755#[derive(Debug, Clone)]
757pub struct OpDistinct {
758 pub sub_op: Box<Op>,
759}
760
761impl OpDistinct {
762 pub fn new(sub_op: Op) -> Self {
764 Self {
765 sub_op: Box::new(sub_op),
766 }
767 }
768}
769
770#[derive(Debug, Clone)]
772pub struct OpReduced {
773 pub sub_op: Box<Op>,
774}
775
776impl OpReduced {
777 pub fn new(sub_op: Op) -> Self {
779 Self {
780 sub_op: Box::new(sub_op),
781 }
782 }
783}
784
785#[derive(Debug, Clone)]
787pub struct OpSlice {
788 pub sub_op: Box<Op>,
789 pub offset: u64,
790 pub limit: Option<u64>,
791}
792
793impl OpSlice {
794 pub fn new(sub_op: Op, offset: u64, limit: Option<u64>) -> Self {
796 Self {
797 sub_op: Box::new(sub_op),
798 offset,
799 limit,
800 }
801 }
802
803 pub fn limit(sub_op: Op, limit: u64) -> Self {
805 Self::new(sub_op, 0, Some(limit))
806 }
807
808 pub fn offset(sub_op: Op, offset: u64) -> Self {
810 Self::new(sub_op, offset, None)
811 }
812}
813
814#[derive(Debug, Clone)]
816pub struct OrderKey {
817 pub expr: ExprTerm,
818 pub ascending: bool,
819}
820
821impl OrderKey {
822 pub fn asc(expr: ExprTerm) -> Self {
824 Self {
825 expr,
826 ascending: true,
827 }
828 }
829
830 pub fn desc(expr: ExprTerm) -> Self {
832 Self {
833 expr,
834 ascending: false,
835 }
836 }
837}
838
839#[derive(Debug, Clone)]
841pub struct OpOrder {
842 pub sub_op: Box<Op>,
843 pub keys: Vec<OrderKey>,
844}
845
846impl OpOrder {
847 pub fn new(sub_op: Op, keys: Vec<OrderKey>) -> Self {
849 Self {
850 sub_op: Box::new(sub_op),
851 keys,
852 }
853 }
854}
855
856#[derive(Debug, Clone)]
858pub enum Aggregate {
859 Count(Option<ExprTerm>),
860 CountDistinct(ExprTerm),
861 Sum(ExprTerm),
862 Avg(ExprTerm),
863 Min(ExprTerm),
864 Max(ExprTerm),
865 Sample(ExprTerm),
866 GroupConcat(ExprTerm, Option<String>),
867}
868
869#[derive(Debug, Clone)]
871pub struct OpGroup {
872 pub sub_op: Box<Op>,
873 pub group_vars: Vec<Var>,
874 pub aggregates: Vec<(Var, Aggregate)>,
875}
876
877impl OpGroup {
878 pub fn new(sub_op: Op, group_vars: Vec<Var>) -> Self {
880 Self {
881 sub_op: Box::new(sub_op),
882 group_vars,
883 aggregates: Vec::new(),
884 }
885 }
886
887 pub fn with_aggregate(mut self, var: Var, agg: Aggregate) -> Self {
889 self.aggregates.push((var, agg));
890 self
891 }
892}
893
894#[derive(Debug, Clone)]
896pub struct OpExtend {
897 pub sub_op: Box<Op>,
898 pub var: Var,
899 pub expr: ExprTerm,
900}
901
902impl OpExtend {
903 pub fn new(sub_op: Op, var: Var, expr: ExprTerm) -> Self {
905 Self {
906 sub_op: Box::new(sub_op),
907 var,
908 expr,
909 }
910 }
911}
912
913#[derive(Debug, Clone)]
915pub struct OpMinus {
916 pub left: Box<Op>,
917 pub right: Box<Op>,
918}
919
920impl OpMinus {
921 pub fn new(left: Op, right: Op) -> Self {
923 Self {
924 left: Box::new(left),
925 right: Box::new(right),
926 }
927 }
928}
929
930#[derive(Debug, Clone)]
932pub struct OpIntersect {
933 pub left: Box<Op>,
934 pub right: Box<Op>,
935}
936
937impl OpIntersect {
938 pub fn new(left: Op, right: Op) -> Self {
940 Self {
941 left: Box::new(left),
942 right: Box::new(right),
943 }
944 }
945}
946
947#[derive(Debug, Clone)]
949pub struct OpTable {
950 pub vars: Vec<Var>,
951 pub rows: Vec<Vec<Option<Value>>>,
952}
953
954impl OpTable {
955 pub fn new(vars: Vec<Var>, rows: Vec<Vec<Option<Value>>>) -> Self {
957 Self { vars, rows }
958 }
959
960 pub fn empty() -> Self {
962 Self {
963 vars: Vec::new(),
964 rows: Vec::new(),
965 }
966 }
967
968 pub fn unit() -> Self {
970 Self {
971 vars: Vec::new(),
972 rows: vec![vec![]],
973 }
974 }
975}
976
977#[derive(Debug, Clone)]
979pub struct OpSequence {
980 pub ops: Vec<Op>,
981}
982
983impl OpSequence {
984 pub fn new(ops: Vec<Op>) -> Self {
986 Self { ops }
987 }
988}
989
990#[derive(Debug, Clone)]
992pub struct OpDisjunction {
993 pub ops: Vec<Op>,
994}
995
996impl OpDisjunction {
997 pub fn new(ops: Vec<Op>) -> Self {
999 Self { ops }
1000 }
1001}
1002
1003#[derive(Debug, Clone, Copy)]
1005pub struct OpNull;
1006
1007impl OpNull {
1008 pub fn new() -> Self {
1010 Self
1011 }
1012}
1013
1014impl Default for OpNull {
1015 fn default() -> Self {
1016 Self::new()
1017 }
1018}
1019
1020#[cfg(test)]
1021mod tests {
1022 use super::*;
1023
1024 #[test]
1025 fn test_triple_pattern() {
1026 let triple = Triple::new(
1027 Pattern::Var(Var::new("s")),
1028 Pattern::Uri("http://example.org/knows".to_string()),
1029 Pattern::Var(Var::new("o")),
1030 );
1031
1032 assert_eq!(triple.vars().len(), 2);
1033 assert!(!triple.is_concrete());
1034 }
1035
1036 #[test]
1037 fn test_bgp() {
1038 let mut bgp = OpBGP::new();
1039 bgp.add(Triple::new(
1040 Pattern::Var(Var::new("s")),
1041 Pattern::Uri("http://example.org/name".to_string()),
1042 Pattern::Var(Var::new("name")),
1043 ));
1044 bgp.add(Triple::new(
1045 Pattern::Var(Var::new("s")),
1046 Pattern::Uri("http://example.org/age".to_string()),
1047 Pattern::Var(Var::new("age")),
1048 ));
1049
1050 assert_eq!(bgp.triples.len(), 2);
1051 assert_eq!(bgp.vars().len(), 3); }
1053
1054 #[test]
1055 fn test_filter_expr() {
1056 let binding = Binding::one(Var::new("x"), Value::Integer(10));
1057
1058 let expr = FilterExpr::Gt(
1059 ExprTerm::Var(Var::new("x")),
1060 ExprTerm::Const(Value::Integer(5)),
1061 );
1062
1063 assert!(expr.evaluate(&binding));
1064
1065 let expr2 = FilterExpr::Lt(
1066 ExprTerm::Var(Var::new("x")),
1067 ExprTerm::Const(Value::Integer(5)),
1068 );
1069
1070 assert!(!expr2.evaluate(&binding));
1071 }
1072
1073 #[test]
1074 fn test_filter_and_or() {
1075 let binding = Binding::two(
1076 Var::new("x"),
1077 Value::Integer(10),
1078 Var::new("y"),
1079 Value::Integer(20),
1080 );
1081
1082 let expr = FilterExpr::and(
1083 FilterExpr::Gt(
1084 ExprTerm::Var(Var::new("x")),
1085 ExprTerm::Const(Value::Integer(5)),
1086 ),
1087 FilterExpr::Lt(
1088 ExprTerm::Var(Var::new("y")),
1089 ExprTerm::Const(Value::Integer(30)),
1090 ),
1091 );
1092
1093 assert!(expr.evaluate(&binding));
1094 }
1095
1096 #[test]
1097 fn test_join_all() {
1098 let op1 = Op::BGP(OpBGP::new());
1099 let op2 = Op::BGP(OpBGP::new());
1100 let op3 = Op::BGP(OpBGP::new());
1101
1102 let joined = OpJoin::join_all(vec![op1, op2, op3]);
1103 assert!(matches!(joined, Op::Join(_)));
1104 }
1105
1106 #[test]
1107 fn test_op_vars() {
1108 let mut bgp = OpBGP::new();
1109 bgp.add(Triple::new(
1110 Pattern::Var(Var::new("s")),
1111 Pattern::Uri("pred".to_string()),
1112 Pattern::Var(Var::new("o")),
1113 ));
1114
1115 let filter = Op::Filter(OpFilter::new(FilterExpr::True, Op::BGP(bgp)));
1116
1117 let vars = filter.vars();
1118 assert!(vars.contains(&Var::new("s")));
1119 assert!(vars.contains(&Var::new("o")));
1120 }
1121
1122 #[test]
1123 fn test_table_op() {
1124 let table = OpTable::new(
1125 vec![Var::new("x"), Var::new("y")],
1126 vec![
1127 vec![Some(Value::Integer(1)), Some(Value::Integer(2))],
1128 vec![Some(Value::Integer(3)), None],
1129 ],
1130 );
1131
1132 assert_eq!(table.vars.len(), 2);
1133 assert_eq!(table.rows.len(), 2);
1134 }
1135
1136 #[test]
1137 fn test_string_functions() {
1138 let binding = Binding::one(Var::new("s"), Value::String("Hello World".to_string()));
1139
1140 let lower = ExprTerm::LCase(Box::new(ExprTerm::Var(Var::new("s"))));
1141 assert_eq!(
1142 lower.evaluate(&binding),
1143 Some(Value::String("hello world".to_string()))
1144 );
1145
1146 let upper = ExprTerm::UCase(Box::new(ExprTerm::Var(Var::new("s"))));
1147 assert_eq!(
1148 upper.evaluate(&binding),
1149 Some(Value::String("HELLO WORLD".to_string()))
1150 );
1151
1152 let len = ExprTerm::StrLen(Box::new(ExprTerm::Var(Var::new("s"))));
1153 assert_eq!(len.evaluate(&binding), Some(Value::Integer(11)));
1154 }
1155}