1mod bool_set;
2
3use crate::event::{EventVisitor, VisitStringResult};
4use crate::scanner::scope::Scope;
5use crate::scoped_ruleset::bool_set::BoolSet;
6use crate::{Event, Path, PathSegment};
7use ahash::AHashMap;
8
9#[derive(Debug)]
12pub struct ScopedRuleSet {
13 tree: RuleTree,
14 num_rules: usize,
16 add_implicit_index_wildcards: bool,
17}
18
19impl ScopedRuleSet {
20 pub fn new(rules_scopes: &[Scope]) -> Self {
22 let mut tree = RuleTree::new();
23
24 for (rule_index, scope) in rules_scopes.iter().enumerate() {
25 match scope {
26 Scope::Include { include, exclude } => {
27 for path in exclude {
28 tree.insert_rule_removal(path, rule_index);
29 }
30
31 for path in include {
32 tree.insert_rule_add(path, rule_index);
33 }
34 }
35 Scope::Exclude(paths) => {
36 tree.insert_rule_add(&Path::root(), rule_index);
37 for path in paths {
38 tree.insert_rule_removal(path, rule_index);
39 }
40 }
41 }
42 }
43 Self {
44 tree,
45 num_rules: rules_scopes.len(),
46 add_implicit_index_wildcards: false,
47 }
48 }
49
50 pub fn with_implicit_index_wildcards(mut self, value: bool) -> Self {
51 self.add_implicit_index_wildcards = value;
52 self
53 }
54
55 pub fn visit_string_rule_combinations<'path, 'c: 'path>(
56 &'c self,
57 event: &'path mut impl Event,
58 content_visitor: impl ContentVisitor<'path>,
59 ) {
60 let bool_set = if self.add_implicit_index_wildcards {
61 Some(BoolSet::new(self.num_rules))
62 } else {
63 None
64 };
65 let mut visitor = ScopedRuledSetEventVisitor {
66 content_visitor,
67 tree_nodes: vec![ActiveRuleTree {
68 rule_tree: &self.tree,
69 index_wildcard_match: false,
70 }],
71 active_node_counter: vec![NodeCounter {
72 active_tree_count: 1,
73 }],
74 path: Path::root(),
75 bool_set,
76 add_implicit_index_wildcards: self.add_implicit_index_wildcards,
77 };
79
80 event.visit_event(&mut visitor)
81 }
82}
83
84pub struct ExclusionCheck<'a> {
85 tree_nodes: &'a [ActiveRuleTree<'a>],
86}
87
88impl ExclusionCheck<'_> {
89 pub fn is_excluded(&self, rule_index: usize) -> bool {
90 for include_node in self.tree_nodes {
91 for change in &include_node.rule_tree.rule_changes {
92 match change {
93 RuleChange::Add(_) => { }
94 RuleChange::Remove(i) => {
95 if *i == rule_index {
96 return true;
97 }
98 }
99 }
100 }
101 }
102 false
103 }
104}
105
106pub trait ContentVisitor<'path> {
107 fn visit_content<'content_visitor>(
108 &'content_visitor mut self,
109 path: &Path<'path>,
110 content: &str,
111 rules: RuleIndexVisitor,
112 is_excluded: ExclusionCheck<'content_visitor>,
113 ) -> bool;
114}
115
116struct ActiveRuleTree<'a> {
118 rule_tree: &'a RuleTree,
119 index_wildcard_match: bool,
122}
123
124struct NodeCounter {
125 active_tree_count: usize,
130}
131
132struct ScopedRuledSetEventVisitor<'a, C> {
133 content_visitor: C,
135
136 tree_nodes: Vec<ActiveRuleTree<'a>>,
139
140 active_node_counter: Vec<NodeCounter>,
143
144 path: Path<'a>,
146
147 bool_set: Option<BoolSet>,
149
150 add_implicit_index_wildcards: bool,
151}
152
153impl<'path, C> EventVisitor<'path> for ScopedRuledSetEventVisitor<'path, C>
154where
155 C: ContentVisitor<'path>,
156{
157 fn push_segment(&mut self, segment: PathSegment<'path>) {
158 let num_active_trees = self.active_node_counter.last().unwrap().active_tree_count;
162 let tree_nodes_len = self.tree_nodes.len();
163 let active_trees_range = tree_nodes_len - num_active_trees..tree_nodes_len;
164
165 for tree_index in active_trees_range {
166 if !self.tree_nodes[tree_index].index_wildcard_match || !segment.is_index() {
167 if let Some(child) = self.tree_nodes[tree_index].rule_tree.children.get(&segment) {
168 self.tree_nodes.push(ActiveRuleTree {
169 rule_tree: child,
170 index_wildcard_match: false,
171 });
172 }
173 }
174
175 if self.add_implicit_index_wildcards && segment.is_index() {
176 self.tree_nodes.push(ActiveRuleTree {
179 rule_tree: self.tree_nodes[tree_index].rule_tree,
180 index_wildcard_match: true,
181 });
182 }
183 }
184
185 self.active_node_counter.push(NodeCounter {
187 active_tree_count: self.tree_nodes.len() - tree_nodes_len,
188 });
189
190 self.path.segments.push(segment);
191 }
192
193 fn pop_segment(&mut self) {
194 let node_counter = self.active_node_counter.pop().unwrap();
195 for _ in 0..node_counter.active_tree_count {
196 let _popped = self.tree_nodes.pop();
198 }
199 self.path.segments.pop();
200 }
201
202 fn visit_string<'s>(&'s mut self, value: &str) -> VisitStringResult<'s, 'path> {
203 let will_mutate = self.content_visitor.visit_content(
204 &self.path,
205 value,
206 RuleIndexVisitor {
207 tree_nodes: &self.tree_nodes,
208 used_rule_set: self.bool_set.as_mut(),
209 },
210 ExclusionCheck {
211 tree_nodes: &self.tree_nodes,
212 },
213 );
214 if let Some(bool_set) = &mut self.bool_set {
215 bool_set.reset();
216 }
217 VisitStringResult {
218 might_mutate: will_mutate,
219 path: &self.path,
220 }
221 }
222}
223
224pub struct RuleIndexVisitor<'a> {
225 tree_nodes: &'a Vec<ActiveRuleTree<'a>>,
226 used_rule_set: Option<&'a mut BoolSet>,
227}
228
229impl RuleIndexVisitor<'_> {
230 pub fn visit_rule_indices(&mut self, mut visit: impl FnMut(usize)) {
233 for include_node in self.tree_nodes {
235 if include_node.index_wildcard_match {
236 continue;
238 }
239 for change in &include_node.rule_tree.rule_changes {
240 match change {
241 RuleChange::Add(rule_index) => {
242 if let Some(used_rule_set) = &mut self.used_rule_set {
243 if !used_rule_set.get_and_set(*rule_index) {
244 (visit)(*rule_index);
245 }
246 } else {
247 (visit)(*rule_index);
248 }
249 }
250 RuleChange::Remove(_) => { }
251 }
252 }
253 }
254 }
255}
256
257#[derive(Clone, Copy, PartialEq, Debug)]
258enum RuleChange {
259 Add(usize),
260 Remove(usize),
261}
262
263#[derive(Clone, Debug)]
264struct RuleTree {
265 rule_changes: Vec<RuleChange>,
266 children: AHashMap<PathSegment<'static>, RuleTree>,
267}
268
269impl RuleTree {
270 pub fn new() -> Self {
271 Self {
272 rule_changes: vec![],
273 children: AHashMap::new(),
274 }
275 }
276
277 pub fn insert_rule_add(&mut self, path: &Path<'static>, rule_index: usize) {
278 self.insert_rule_inner(&path.segments, RuleChange::Add(rule_index));
279 }
280
281 pub fn insert_rule_removal(&mut self, path: &Path<'static>, rule_index: usize) {
282 self.insert_rule_inner(&path.segments, RuleChange::Remove(rule_index));
283 }
284
285 fn insert_rule_inner(&mut self, path: &[PathSegment<'static>], rule_change: RuleChange) {
286 if self.rule_changes.contains(&rule_change) {
287 return;
288 }
289 if let Some((first, remaining)) = path.split_first() {
290 let child_tree = self
291 .children
292 .entry(first.clone())
293 .or_insert_with(RuleTree::new);
294 child_tree.insert_rule_inner(remaining, rule_change);
295 } else {
296 self.recursively_remove(rule_change);
298 self.rule_changes.push(rule_change);
299 }
300 }
301
302 fn recursively_remove(&mut self, rule_change: RuleChange) {
305 self.rule_changes.retain(|x| *x != rule_change);
306 for child_tree in self.children.values_mut() {
307 child_tree.recursively_remove(rule_change);
308 }
309 }
310}
311
312#[cfg(test)]
313mod test {
314 use crate::simple_event::SimpleEvent;
315
316 use super::*;
317
318 #[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
319 struct VisitedPath {
320 path: Path<'static>,
321 content: String,
322 rules: Vec<VisitedRule>,
323 }
324
325 #[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
326 struct VisitedRule {
327 rule_index: usize,
328 is_excluded: bool,
329 }
330
331 #[derive(Debug, PartialEq, Eq)]
332 struct Visited {
333 paths: Vec<VisitedPath>,
334 }
335
336 fn visit_event(event: &mut impl Event, ruleset: &ScopedRuleSet) -> Visited {
338 let mut visited = Visited { paths: vec![] };
339
340 struct RecordingContentVisitor<'a> {
341 visited: &'a mut Visited,
342 }
343
344 impl<'a> ContentVisitor<'a> for RecordingContentVisitor<'a> {
345 fn visit_content<'content_visitor>(
346 &'content_visitor mut self,
347 path: &Path<'a>,
348 content: &str,
349 mut rule_iter: RuleIndexVisitor,
350 exclusion_check: ExclusionCheck<'content_visitor>,
351 ) -> bool {
352 let mut rules = vec![];
353 rule_iter.visit_rule_indices(|rule_index| {
354 rules.push(VisitedRule {
355 rule_index,
356 is_excluded: exclusion_check.is_excluded(rule_index),
357 });
358 });
359 rules.sort();
360 self.visited.paths.push(VisitedPath {
361 path: path.into_static(),
362 content: content.to_string(),
363 rules,
364 });
365 true
366 }
367 }
368
369 ruleset.visit_string_rule_combinations(
370 event,
371 RecordingContentVisitor {
372 visited: &mut visited,
373 },
374 );
375 visited.paths.sort();
376 visited
377 }
378
379 #[test]
380 fn test_inclusive_scopes() {
381 let ruleset = ScopedRuleSet::new(&[
383 Scope::include(vec![Path::from(vec!["a".into()])]),
384 Scope::include(vec![Path::from(vec!["a".into(), "b".into()])]),
385 Scope::include(vec![]),
386 ]);
387
388 let mut event = SimpleEvent::Map(
389 [
390 (
391 "a".into(),
392 SimpleEvent::Map(
393 [
394 ("b".into(), SimpleEvent::String("value-ab".into())),
395 ("c".into(), SimpleEvent::String("value-ac".into())),
396 ]
397 .into(),
398 ),
399 ),
400 ("d".into(), SimpleEvent::String("value-d".into())),
401 ]
402 .into(),
403 );
404
405 let paths = visit_event(&mut event, &ruleset);
406
407 assert_eq!(
408 paths,
409 Visited {
410 paths: vec![
411 VisitedPath {
412 path: Path::from(vec!["a".into(), "b".into()]),
413 content: "value-ab".into(),
414 rules: vec![
415 VisitedRule {
416 rule_index: 0,
417 is_excluded: false
418 },
419 VisitedRule {
420 rule_index: 1,
421 is_excluded: false
422 }
423 ]
424 },
425 VisitedPath {
426 path: Path::from(vec!["a".into(), "c".into()]),
427 content: "value-ac".into(),
428 rules: vec![VisitedRule {
429 rule_index: 0,
430 is_excluded: false
431 }]
432 },
433 VisitedPath {
434 path: Path::from(vec!["d".into()]),
435 content: "value-d".into(),
436 rules: vec![]
437 }
438 ],
439 }
440 );
441 }
442
443 #[test]
444 fn test_inclusive_scopes_array() {
445 let ruleset = ScopedRuleSet::new(&[
447 Scope::include(vec![Path::from(vec![0.into()])]),
448 Scope::include(vec![Path::from(vec![1.into(), 0.into()])]),
449 Scope::include(vec![Path::from(vec![2.into(), 0.into()])]),
450 ]);
451
452 let mut event = SimpleEvent::List(vec![
453 SimpleEvent::String("value-0".into()),
454 SimpleEvent::String("value-1".into()),
455 SimpleEvent::List(vec![SimpleEvent::String("value-2-0".into())]),
456 SimpleEvent::String("value-3".into()),
457 ]);
458
459 let paths = visit_event(&mut event, &ruleset);
460
461 assert_eq!(
462 paths,
463 Visited {
464 paths: vec![
465 VisitedPath {
466 path: Path::from(vec![0.into()]),
467 content: "value-0".into(),
468 rules: vec![VisitedRule {
469 rule_index: 0,
470 is_excluded: false
471 }]
472 },
473 VisitedPath {
474 path: Path::from(vec![1.into()]),
475 content: "value-1".into(),
476 rules: vec![]
477 },
478 VisitedPath {
479 path: Path::from(vec![2.into(), 0.into()]),
480 content: "value-2-0".into(),
481 rules: vec![VisitedRule {
482 rule_index: 2,
483 is_excluded: false
484 }]
485 },
486 VisitedPath {
487 path: Path::from(vec![3.into()]),
488 content: "value-3".into(),
489 rules: vec![]
490 }
491 ],
492 }
493 );
494 }
495
496 #[test]
497 fn test_exclusive_scopes() {
498 let ruleset = ScopedRuleSet::new(&[
500 Scope::exclude(vec![Path::from(vec!["a".into()])]),
501 Scope::exclude(vec![Path::from(vec!["a".into(), "b".into()])]),
502 Scope::all(),
504 ]);
505
506 let mut event = SimpleEvent::Map(
507 [
508 (
509 "a".into(),
510 SimpleEvent::Map(
511 [
512 ("b".into(), SimpleEvent::String("value-ab".into())),
513 ("c".into(), SimpleEvent::String("value-ac".into())),
514 ]
515 .into(),
516 ),
517 ),
518 ("d".into(), SimpleEvent::String("value-d".into())),
519 ("e".into(), SimpleEvent::List(vec![])),
520 ]
521 .into(),
522 );
523
524 let paths = visit_event(&mut event, &ruleset);
525
526 assert_eq!(
527 paths,
528 Visited {
529 paths: vec![
530 VisitedPath {
531 path: Path::from(vec!["a".into(), "b".into()]),
532 content: "value-ab".into(),
533 rules: vec![
534 VisitedRule {
535 rule_index: 0,
536 is_excluded: true
537 },
538 VisitedRule {
539 rule_index: 1,
540 is_excluded: true
541 },
542 VisitedRule {
543 rule_index: 2,
544 is_excluded: false
545 }
546 ]
547 },
548 VisitedPath {
549 path: Path::from(vec!["a".into(), "c".into()]),
550 content: "value-ac".into(),
551 rules: vec![
552 VisitedRule {
553 rule_index: 0,
554 is_excluded: true
555 },
556 VisitedRule {
557 rule_index: 1,
558 is_excluded: false
559 },
560 VisitedRule {
561 rule_index: 2,
562 is_excluded: false
563 }
564 ]
565 },
566 VisitedPath {
567 path: Path::from(vec!["d".into()]),
568 content: "value-d".into(),
569 rules: vec![
570 VisitedRule {
571 rule_index: 0,
572 is_excluded: false
573 },
574 VisitedRule {
575 rule_index: 1,
576 is_excluded: false
577 },
578 VisitedRule {
579 rule_index: 2,
580 is_excluded: false
581 }
582 ]
583 }
584 ],
585 },
586 );
587 }
588
589 #[test]
590 fn test_exclusive_scopes_array() {
591 let ruleset = ScopedRuleSet::new(&[
593 Scope::exclude(vec![Path::from(vec![0.into()])]),
594 Scope::exclude(vec![Path::from(vec![1.into(), 0.into()])]),
595 Scope::exclude(vec![Path::from(vec![2.into(), 0.into()])]),
596 ]);
597
598 let mut event = SimpleEvent::List(vec![
599 SimpleEvent::String("value-0".into()),
600 SimpleEvent::String("value-1".into()),
601 SimpleEvent::List(vec![SimpleEvent::String("value-2-0".into())]),
602 SimpleEvent::String("value-3".into()),
603 ]);
604
605 let paths = visit_event(&mut event, &ruleset);
606
607 assert_eq!(
608 paths,
609 Visited {
610 paths: vec![
611 VisitedPath {
612 path: Path::from(vec![0.into()]),
613 content: "value-0".into(),
614 rules: vec![
615 VisitedRule {
616 rule_index: 0,
617 is_excluded: true
618 },
619 VisitedRule {
620 rule_index: 1,
621 is_excluded: false
622 },
623 VisitedRule {
624 rule_index: 2,
625 is_excluded: false
626 }
627 ]
628 },
629 VisitedPath {
630 path: Path::from(vec![1.into()]),
631 content: "value-1".into(),
632 rules: vec![
633 VisitedRule {
634 rule_index: 0,
635 is_excluded: false
636 },
637 VisitedRule {
638 rule_index: 1,
639 is_excluded: false
640 },
641 VisitedRule {
642 rule_index: 2,
643 is_excluded: false
644 }
645 ]
646 },
647 VisitedPath {
648 path: Path::from(vec![2.into(), 0.into()]),
649 content: "value-2-0".into(),
650 rules: vec![
651 VisitedRule {
652 rule_index: 0,
653 is_excluded: false
654 },
655 VisitedRule {
656 rule_index: 1,
657 is_excluded: false
658 },
659 VisitedRule {
660 rule_index: 2,
661 is_excluded: true
662 }
663 ]
664 },
665 VisitedPath {
666 path: Path::from(vec![3.into()]),
667 content: "value-3".into(),
668 rules: vec![
669 VisitedRule {
670 rule_index: 0,
671 is_excluded: false
672 },
673 VisitedRule {
674 rule_index: 1,
675 is_excluded: false
676 },
677 VisitedRule {
678 rule_index: 2,
679 is_excluded: false
680 }
681 ]
682 }
683 ],
684 }
685 );
686 }
687
688 #[test]
689 fn test_include_and_exclude() {
690 let ruleset = ScopedRuleSet::new(&[Scope::include_and_exclude(
693 vec![Path::from(vec![2.into()])],
694 vec![Path::from(vec![2.into(), 0.into()])],
695 )]);
696
697 let mut event = SimpleEvent::List(vec![
698 SimpleEvent::String("value-0".into()),
699 SimpleEvent::String("value-1".into()),
700 SimpleEvent::List(vec![
701 SimpleEvent::String("value-2-0".into()),
702 SimpleEvent::String("value-2-1".into()),
703 ]),
704 SimpleEvent::String("value-3".into()),
705 ]);
706
707 let paths = visit_event(&mut event, &ruleset);
708
709 assert_eq!(
710 paths,
711 Visited {
712 paths: vec![
713 VisitedPath {
714 path: Path::from(vec![0.into()]),
715 content: "value-0".into(),
716 rules: vec![]
717 },
718 VisitedPath {
719 path: Path::from(vec![1.into()]),
720 content: "value-1".into(),
721 rules: vec![]
722 },
723 VisitedPath {
724 path: Path::from(vec![2.into(), 0.into()]),
725 content: "value-2-0".into(),
726 rules: vec![VisitedRule {
727 rule_index: 0,
728 is_excluded: true
729 }]
730 },
731 VisitedPath {
732 path: Path::from(vec![2.into(), 1.into()]),
733 content: "value-2-1".into(),
734 rules: vec![VisitedRule {
735 rule_index: 0,
736 is_excluded: false
737 }]
738 },
739 VisitedPath {
740 path: Path::from(vec![3.into()]),
741 content: "value-3".into(),
742 rules: vec![]
743 },
744 ]
745 }
746 );
747 }
748
749 #[test]
750 fn test_include_and_exclude_priority() {
751 let ruleset = ScopedRuleSet::new(&[Scope::include_and_exclude(
754 vec![Path::from(vec![1.into(), 0.into()])],
756 vec![Path::from(vec![1.into()])],
757 )]);
758
759 let mut event = SimpleEvent::List(vec![
760 SimpleEvent::String("value-0".into()),
761 SimpleEvent::List(vec![
762 SimpleEvent::String("value-1-0".into()),
763 SimpleEvent::String("value-1-1".into()),
764 ]),
765 ]);
766
767 let paths = visit_event(&mut event, &ruleset);
768
769 assert_eq!(
770 paths,
771 Visited {
772 paths: vec![
773 VisitedPath {
774 path: Path::from(vec![0.into()]),
775 content: "value-0".into(),
776 rules: vec![]
777 },
778 VisitedPath {
779 path: Path::from(vec![1.into(), 0.into()]),
780 content: "value-1-0".into(),
781 rules: vec![VisitedRule {
782 rule_index: 0,
783 is_excluded: true
784 }]
785 },
786 VisitedPath {
787 path: Path::from(vec![1.into(), 1.into()]),
788 content: "value-1-1".into(),
789 rules: vec![]
790 }
791 ],
792 }
793 );
794 }
795
796 #[test]
797 fn test_include_same_change_multiple_times() {
798 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![
801 Path::from(vec!["a".into()]),
802 Path::from(vec!["a".into(), "b".into()]),
803 ])]);
804
805 let mut event = SimpleEvent::Map(
806 [
807 (
808 "a".into(),
809 SimpleEvent::Map(
810 [
811 ("b".into(), SimpleEvent::String("value-ab".into())),
812 ("c".into(), SimpleEvent::String("value-ac".into())),
813 ]
814 .into(),
815 ),
816 ),
817 ("d".into(), SimpleEvent::String("value-d".into())),
818 ]
819 .into(),
820 );
821
822 let paths = visit_event(&mut event, &ruleset);
823
824 assert_eq!(
825 paths,
826 Visited {
827 paths: vec![
828 VisitedPath {
829 path: Path::from(vec!["a".into(), "b".into()]),
830 content: "value-ab".into(),
831 rules: vec![VisitedRule {
832 rule_index: 0,
833 is_excluded: false
834 }]
835 },
836 VisitedPath {
837 path: Path::from(vec!["a".into(), "c".into()]),
838 content: "value-ac".into(),
839 rules: vec![VisitedRule {
840 rule_index: 0,
841 is_excluded: false
842 }]
843 },
844 VisitedPath {
845 path: Path::from(vec!["d".into()]),
846 content: "value-d".into(),
847 rules: vec![]
848 }
849 ],
850 }
851 );
852 }
853
854 #[test]
855 fn test_include_root_multiple_times() {
856 let ruleset =
858 ScopedRuleSet::new(&[Scope::include(vec![Path::from(vec![]), Path::from(vec![])])]);
859
860 let mut event = SimpleEvent::Map(
861 [
862 (
863 "a".into(),
864 SimpleEvent::Map(
865 [
866 ("b".into(), SimpleEvent::String("value-ab".into())),
867 ("c".into(), SimpleEvent::String("value-ac".into())),
868 ]
869 .into(),
870 ),
871 ),
872 ("d".into(), SimpleEvent::String("value-d".into())),
873 ]
874 .into(),
875 );
876
877 let paths = visit_event(&mut event, &ruleset);
878
879 assert_eq!(
880 paths,
881 Visited {
882 paths: vec![
883 VisitedPath {
884 path: Path::from(vec!["a".into(), "b".into()]),
885 content: "value-ab".into(),
886 rules: vec![VisitedRule {
887 rule_index: 0,
888 is_excluded: false
889 }]
890 },
891 VisitedPath {
892 path: Path::from(vec!["a".into(), "c".into()]),
893 content: "value-ac".into(),
894 rules: vec![VisitedRule {
895 rule_index: 0,
896 is_excluded: false
897 }]
898 },
899 VisitedPath {
900 path: Path::from(vec!["d".into()]),
901 content: "value-d".into(),
902 rules: vec![VisitedRule {
903 rule_index: 0,
904 is_excluded: false
905 }]
906 }
907 ],
908 }
909 );
910 }
911
912 #[test]
913 fn test_include_same_change_multiple_times_reversed() {
914 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![
919 Path::from(vec!["a".into(), "b".into()]),
920 Path::from(vec!["a".into()]),
921 ])]);
922
923 let mut event = SimpleEvent::Map(
924 [
925 (
926 "a".into(),
927 SimpleEvent::Map(
928 [
929 ("b".into(), SimpleEvent::String("value-ab".into())),
930 ("c".into(), SimpleEvent::String("value-ac".into())),
931 ]
932 .into(),
933 ),
934 ),
935 ("d".into(), SimpleEvent::String("value-d".into())),
936 ]
937 .into(),
938 );
939
940 let paths = visit_event(&mut event, &ruleset);
941
942 assert_eq!(
943 paths,
944 Visited {
945 paths: vec![
946 VisitedPath {
947 path: Path::from(vec!["a".into(), "b".into()]),
948 content: "value-ab".into(),
949 rules: vec![VisitedRule {
950 rule_index: 0,
951 is_excluded: false
952 }]
953 },
954 VisitedPath {
955 path: Path::from(vec!["a".into(), "c".into()]),
956 content: "value-ac".into(),
957 rules: vec![VisitedRule {
958 rule_index: 0,
959 is_excluded: false
960 }]
961 },
962 VisitedPath {
963 path: Path::from(vec!["d".into()]),
964 content: "value-d".into(),
965 rules: vec![]
966 }
967 ],
968 }
969 );
970 }
971
972 #[test]
973 fn test_fields_should_act_as_wildcard_on_lists() {
974 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![Path::from(vec![
975 "a".into(),
976 "b".into(),
977 ])])])
978 .with_implicit_index_wildcards(true);
979
980 let mut event = SimpleEvent::Map(
981 [(
982 "a".into(),
983 SimpleEvent::List(vec![SimpleEvent::Map(
984 [("b".into(), SimpleEvent::String("value-a-0-b".to_string()))].into(),
985 )]),
986 )]
987 .into(),
988 );
989
990 let paths = visit_event(&mut event, &ruleset);
991
992 assert_eq!(
993 paths,
994 Visited {
995 paths: vec![VisitedPath {
996 path: Path::from(vec!["a".into(), 0.into(), "b".into()]),
997 content: "value-a-0-b".into(),
998 rules: vec![VisitedRule {
1000 rule_index: 0,
1001 is_excluded: false
1002 }]
1003 },],
1004 }
1005 );
1006 }
1007
1008 #[test]
1009 fn test_exclude_implicit_wildcard_path() {
1010 let ab_path = Path::from(vec!["a".into(), "b".into()]);
1014 let ruleset = ScopedRuleSet::new(&[Scope::include_and_exclude(
1015 vec![ab_path.clone()],
1016 vec![ab_path],
1017 )])
1018 .with_implicit_index_wildcards(true);
1019
1020 let mut event = SimpleEvent::Map(
1021 [(
1022 "a".into(),
1023 SimpleEvent::List(vec![SimpleEvent::Map(
1024 [("b".into(), SimpleEvent::String("value-a-0-b".to_string()))].into(),
1025 )]),
1026 )]
1027 .into(),
1028 );
1029
1030 let paths = visit_event(&mut event, &ruleset);
1031
1032 assert_eq!(
1033 paths,
1034 Visited {
1035 paths: vec![VisitedPath {
1036 path: Path::from(vec!["a".into(), 0.into(), "b".into()]),
1037 content: "value-a-0-b".into(),
1038 rules: vec![VisitedRule {
1040 rule_index: 0,
1041 is_excluded: true
1042 }]
1043 },],
1044 }
1045 );
1046 }
1047
1048 #[test]
1049 fn test_included_scope_both_implicit_and_explicit_index() {
1050 let a_0_c_path = Path::from(vec!["a".into(), 0.into(), "c".into()]);
1051 let ab_path = Path::from(vec!["a".into(), "b".into()]);
1052 let a_1_d_path = Path::from(vec!["a".into(), 1.into(), "d".into()]);
1053
1054 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![a_0_c_path, ab_path, a_1_d_path])])
1055 .with_implicit_index_wildcards(true);
1056
1057 let mut event = SimpleEvent::Map(
1058 [(
1059 "a".into(),
1060 SimpleEvent::List(vec![
1061 SimpleEvent::Map(
1062 [
1063 ("b".into(), SimpleEvent::String("value-a-0-b".to_string())),
1064 ("c".into(), SimpleEvent::String("value-a-0-c".to_string())),
1065 ("d".into(), SimpleEvent::String("value-a-0-d".to_string())),
1066 ]
1067 .into(),
1068 ),
1069 SimpleEvent::Map(
1070 [
1071 ("b".into(), SimpleEvent::String("value-a-1-b".to_string())),
1072 ("c".into(), SimpleEvent::String("value-a-1-c".to_string())),
1073 ("d".into(), SimpleEvent::String("value-a-1-d".to_string())),
1074 ]
1075 .into(),
1076 ),
1077 ]),
1078 )]
1079 .into(),
1080 );
1081
1082 let paths = visit_event(&mut event, &ruleset);
1083
1084 assert_eq!(
1085 paths,
1086 Visited {
1087 paths: vec![
1088 VisitedPath {
1089 path: Path::from(vec!["a".into(), 0.into(), "b".into()]),
1090 content: "value-a-0-b".into(),
1091 rules: vec![VisitedRule {
1092 rule_index: 0,
1093 is_excluded: false
1094 }]
1095 },
1096 VisitedPath {
1097 path: Path::from(vec!["a".into(), 0.into(), "c".into()]),
1098 content: "value-a-0-c".into(),
1099 rules: vec![VisitedRule {
1100 rule_index: 0,
1101 is_excluded: false
1102 }]
1103 },
1104 VisitedPath {
1105 path: Path::from(vec!["a".into(), 0.into(), "d".into()]),
1106 content: "value-a-0-d".into(),
1107 rules: vec![]
1108 },
1109 VisitedPath {
1110 path: Path::from(vec!["a".into(), 1.into(), "b".into()]),
1111 content: "value-a-1-b".into(),
1112 rules: vec![VisitedRule {
1113 rule_index: 0,
1114 is_excluded: false
1115 }]
1116 },
1117 VisitedPath {
1118 path: Path::from(vec!["a".into(), 1.into(), "c".into()]),
1119 content: "value-a-1-c".into(),
1120 rules: vec![]
1121 },
1122 VisitedPath {
1123 path: Path::from(vec!["a".into(), 1.into(), "d".into()]),
1124 content: "value-a-1-d".into(),
1125 rules: vec![VisitedRule {
1126 rule_index: 0,
1127 is_excluded: false
1128 }]
1129 },
1130 ],
1131 }
1132 );
1133 }
1134
1135 #[test]
1136 fn test_duplicate_rules_are_filtered_out() {
1137 let a_b_path = Path::from(vec!["a".into(), "b".into()]);
1142 let a_0_b_path = Path::from(vec!["a".into(), 0.into(), "b".into()]);
1143
1144 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![a_b_path, a_0_b_path])])
1145 .with_implicit_index_wildcards(true);
1146
1147 let mut event = SimpleEvent::Map(
1148 [(
1149 "a".into(),
1150 SimpleEvent::List(vec![SimpleEvent::Map(
1151 [("b".into(), SimpleEvent::String("value-a-0-b".to_string()))].into(),
1152 )]),
1153 )]
1154 .into(),
1155 );
1156
1157 let paths = visit_event(&mut event, &ruleset);
1158
1159 assert_eq!(
1160 paths,
1161 Visited {
1162 paths: vec![VisitedPath {
1163 path: Path::from(vec!["a".into(), 0.into(), "b".into()]),
1164 content: "value-a-0-b".into(),
1165 rules: vec![VisitedRule {
1166 rule_index: 0,
1167 is_excluded: false
1168 }]
1169 },],
1170 }
1171 );
1172 }
1173
1174 #[test]
1175 fn test_deeply_nested_implicit_wildcard_index() {
1176 let a_b_path = Path::from(vec!["a".into(), "b".into()]);
1179 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![a_b_path])])
1180 .with_implicit_index_wildcards(true);
1181
1182 let mut event = SimpleEvent::Map(
1183 [(
1184 "a".into(),
1185 SimpleEvent::List(vec![SimpleEvent::List(vec![SimpleEvent::Map(
1186 [("b".into(), SimpleEvent::String("value-a-0-0-b".to_string()))].into(),
1187 )])]),
1188 )]
1189 .into(),
1190 );
1191
1192 let paths = visit_event(&mut event, &ruleset);
1193
1194 assert_eq!(
1195 paths,
1196 Visited {
1197 paths: vec![VisitedPath {
1198 path: Path::from(vec!["a".into(), 0.into(), 0.into(), "b".into()]),
1199 content: "value-a-0-0-b".into(),
1200 rules: vec![VisitedRule {
1201 rule_index: 0,
1202 is_excluded: false
1203 }]
1204 },],
1205 }
1206 );
1207 }
1208
1209 #[test]
1210 fn test_implicit_index_wildcard_is_disabled_by_default() {
1211 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![Path::from(vec![
1212 "a".into(),
1213 "b".into(),
1214 ])])]);
1215
1216 let mut event = SimpleEvent::Map(
1217 [(
1218 "a".into(),
1219 SimpleEvent::List(vec![SimpleEvent::Map(
1220 [("b".into(), SimpleEvent::String("value-a-0-b".to_string()))].into(),
1221 )]),
1222 )]
1223 .into(),
1224 );
1225
1226 let paths = visit_event(&mut event, &ruleset);
1227
1228 assert_eq!(
1229 paths,
1230 Visited {
1231 paths: vec![VisitedPath {
1232 path: Path::from(vec!["a".into(), 0.into(), "b".into()]),
1233 content: "value-a-0-b".into(),
1234 rules: vec![]
1236 },],
1237 }
1238 );
1239 }
1240}