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, ScannerError};
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 ) -> Result<(), ScannerError> {
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 ) -> Result<bool, ScannerError>;
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>(
203 &'s mut self,
204 value: &str,
205 ) -> Result<VisitStringResult<'s, 'path>, ScannerError> {
206 let will_mutate = self.content_visitor.visit_content(
207 &self.path,
208 value,
209 RuleIndexVisitor {
210 tree_nodes: &self.tree_nodes,
211 used_rule_set: self.bool_set.as_mut(),
212 },
213 ExclusionCheck {
214 tree_nodes: &self.tree_nodes,
215 },
216 );
217 if let Some(bool_set) = &mut self.bool_set {
218 bool_set.reset();
219 }
220 will_mutate.map(|will_mutate| VisitStringResult {
221 might_mutate: will_mutate,
222 path: &self.path,
223 })
224 }
225}
226
227pub struct RuleIndexVisitor<'a> {
228 tree_nodes: &'a Vec<ActiveRuleTree<'a>>,
229 used_rule_set: Option<&'a mut BoolSet>,
230}
231
232impl RuleIndexVisitor<'_> {
233 pub fn visit_rule_indices(
236 &mut self,
237 mut visit: impl FnMut(usize) -> Result<(), ScannerError>,
238 ) -> Result<(), ScannerError> {
239 for include_node in self.tree_nodes {
241 if include_node.index_wildcard_match {
242 continue;
244 }
245 for change in &include_node.rule_tree.rule_changes {
246 match change {
247 RuleChange::Add(rule_index) => {
248 if let Some(used_rule_set) = &mut self.used_rule_set {
249 if !used_rule_set.get_and_set(*rule_index) {
250 (visit)(*rule_index)?;
251 }
252 } else {
253 (visit)(*rule_index)?;
254 }
255 }
256 RuleChange::Remove(_) => { }
257 }
258 }
259 }
260 Ok(())
261 }
262}
263
264#[derive(Clone, Copy, PartialEq, Debug)]
265enum RuleChange {
266 Add(usize),
267 Remove(usize),
268}
269
270#[derive(Clone, Debug)]
271struct RuleTree {
272 rule_changes: Vec<RuleChange>,
273 children: AHashMap<PathSegment<'static>, RuleTree>,
274}
275
276impl RuleTree {
277 pub fn new() -> Self {
278 Self {
279 rule_changes: vec![],
280 children: AHashMap::new(),
281 }
282 }
283
284 pub fn insert_rule_add(&mut self, path: &Path<'static>, rule_index: usize) {
285 self.insert_rule_inner(&path.segments, RuleChange::Add(rule_index));
286 }
287
288 pub fn insert_rule_removal(&mut self, path: &Path<'static>, rule_index: usize) {
289 self.insert_rule_inner(&path.segments, RuleChange::Remove(rule_index));
290 }
291
292 fn insert_rule_inner(&mut self, path: &[PathSegment<'static>], rule_change: RuleChange) {
293 if self.rule_changes.contains(&rule_change) {
294 return;
295 }
296 if let Some((first, remaining)) = path.split_first() {
297 let child_tree = self
298 .children
299 .entry(first.clone())
300 .or_insert_with(RuleTree::new);
301 child_tree.insert_rule_inner(remaining, rule_change);
302 } else {
303 self.recursively_remove(rule_change);
305 self.rule_changes.push(rule_change);
306 }
307 }
308
309 fn recursively_remove(&mut self, rule_change: RuleChange) {
312 self.rule_changes.retain(|x| *x != rule_change);
313 for child_tree in self.children.values_mut() {
314 child_tree.recursively_remove(rule_change);
315 }
316 }
317}
318
319#[cfg(test)]
320mod test {
321 use crate::simple_event::SimpleEvent;
322
323 use super::*;
324
325 #[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
326 struct VisitedPath {
327 path: Path<'static>,
328 content: String,
329 rules: Vec<VisitedRule>,
330 }
331
332 #[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
333 struct VisitedRule {
334 rule_index: usize,
335 is_excluded: bool,
336 }
337
338 #[derive(Debug, PartialEq, Eq)]
339 struct Visited {
340 paths: Vec<VisitedPath>,
341 }
342
343 fn visit_event(event: &mut impl Event, ruleset: &ScopedRuleSet) -> Visited {
345 let mut visited = Visited { paths: vec![] };
346
347 struct RecordingContentVisitor<'a> {
348 visited: &'a mut Visited,
349 }
350
351 impl<'a> ContentVisitor<'a> for RecordingContentVisitor<'a> {
352 fn visit_content<'content_visitor>(
353 &'content_visitor mut self,
354 path: &Path<'a>,
355 content: &str,
356 mut rule_iter: RuleIndexVisitor,
357 exclusion_check: ExclusionCheck<'content_visitor>,
358 ) -> Result<bool, ScannerError> {
359 let mut rules = vec![];
360 rule_iter.visit_rule_indices(|rule_index| {
361 rules.push(VisitedRule {
362 rule_index,
363 is_excluded: exclusion_check.is_excluded(rule_index),
364 });
365 Ok(())
366 })?;
367 rules.sort();
368 self.visited.paths.push(VisitedPath {
369 path: path.into_static(),
370 content: content.to_string(),
371 rules,
372 });
373 Ok(true)
374 }
375 }
376
377 ruleset
378 .visit_string_rule_combinations(
379 event,
380 RecordingContentVisitor {
381 visited: &mut visited,
382 },
383 )
384 .unwrap();
385 visited.paths.sort();
386 visited
387 }
388
389 #[test]
390 fn test_inclusive_scopes() {
391 let ruleset = ScopedRuleSet::new(&[
393 Scope::include(vec![Path::from(vec!["a".into()])]),
394 Scope::include(vec![Path::from(vec!["a".into(), "b".into()])]),
395 Scope::include(vec![]),
396 ]);
397
398 let mut event = SimpleEvent::Map(
399 [
400 (
401 "a".into(),
402 SimpleEvent::Map(
403 [
404 ("b".into(), SimpleEvent::String("value-ab".into())),
405 ("c".into(), SimpleEvent::String("value-ac".into())),
406 ]
407 .into(),
408 ),
409 ),
410 ("d".into(), SimpleEvent::String("value-d".into())),
411 ]
412 .into(),
413 );
414
415 let paths = visit_event(&mut event, &ruleset);
416
417 assert_eq!(
418 paths,
419 Visited {
420 paths: vec![
421 VisitedPath {
422 path: Path::from(vec!["a".into(), "b".into()]),
423 content: "value-ab".into(),
424 rules: vec![
425 VisitedRule {
426 rule_index: 0,
427 is_excluded: false
428 },
429 VisitedRule {
430 rule_index: 1,
431 is_excluded: false
432 }
433 ]
434 },
435 VisitedPath {
436 path: Path::from(vec!["a".into(), "c".into()]),
437 content: "value-ac".into(),
438 rules: vec![VisitedRule {
439 rule_index: 0,
440 is_excluded: false
441 }]
442 },
443 VisitedPath {
444 path: Path::from(vec!["d".into()]),
445 content: "value-d".into(),
446 rules: vec![]
447 }
448 ],
449 }
450 );
451 }
452
453 #[test]
454 fn test_inclusive_scopes_array() {
455 let ruleset = ScopedRuleSet::new(&[
457 Scope::include(vec![Path::from(vec![0.into()])]),
458 Scope::include(vec![Path::from(vec![1.into(), 0.into()])]),
459 Scope::include(vec![Path::from(vec![2.into(), 0.into()])]),
460 ]);
461
462 let mut event = SimpleEvent::List(vec![
463 SimpleEvent::String("value-0".into()),
464 SimpleEvent::String("value-1".into()),
465 SimpleEvent::List(vec![SimpleEvent::String("value-2-0".into())]),
466 SimpleEvent::String("value-3".into()),
467 ]);
468
469 let paths = visit_event(&mut event, &ruleset);
470
471 assert_eq!(
472 paths,
473 Visited {
474 paths: vec![
475 VisitedPath {
476 path: Path::from(vec![0.into()]),
477 content: "value-0".into(),
478 rules: vec![VisitedRule {
479 rule_index: 0,
480 is_excluded: false
481 }]
482 },
483 VisitedPath {
484 path: Path::from(vec![1.into()]),
485 content: "value-1".into(),
486 rules: vec![]
487 },
488 VisitedPath {
489 path: Path::from(vec![2.into(), 0.into()]),
490 content: "value-2-0".into(),
491 rules: vec![VisitedRule {
492 rule_index: 2,
493 is_excluded: false
494 }]
495 },
496 VisitedPath {
497 path: Path::from(vec![3.into()]),
498 content: "value-3".into(),
499 rules: vec![]
500 }
501 ],
502 }
503 );
504 }
505
506 #[test]
507 fn test_exclusive_scopes() {
508 let ruleset = ScopedRuleSet::new(&[
510 Scope::exclude(vec![Path::from(vec!["a".into()])]),
511 Scope::exclude(vec![Path::from(vec!["a".into(), "b".into()])]),
512 Scope::all(),
514 ]);
515
516 let mut event = SimpleEvent::Map(
517 [
518 (
519 "a".into(),
520 SimpleEvent::Map(
521 [
522 ("b".into(), SimpleEvent::String("value-ab".into())),
523 ("c".into(), SimpleEvent::String("value-ac".into())),
524 ]
525 .into(),
526 ),
527 ),
528 ("d".into(), SimpleEvent::String("value-d".into())),
529 ("e".into(), SimpleEvent::List(vec![])),
530 ]
531 .into(),
532 );
533
534 let paths = visit_event(&mut event, &ruleset);
535
536 assert_eq!(
537 paths,
538 Visited {
539 paths: vec![
540 VisitedPath {
541 path: Path::from(vec!["a".into(), "b".into()]),
542 content: "value-ab".into(),
543 rules: vec![
544 VisitedRule {
545 rule_index: 0,
546 is_excluded: true
547 },
548 VisitedRule {
549 rule_index: 1,
550 is_excluded: true
551 },
552 VisitedRule {
553 rule_index: 2,
554 is_excluded: false
555 }
556 ]
557 },
558 VisitedPath {
559 path: Path::from(vec!["a".into(), "c".into()]),
560 content: "value-ac".into(),
561 rules: vec![
562 VisitedRule {
563 rule_index: 0,
564 is_excluded: true
565 },
566 VisitedRule {
567 rule_index: 1,
568 is_excluded: false
569 },
570 VisitedRule {
571 rule_index: 2,
572 is_excluded: false
573 }
574 ]
575 },
576 VisitedPath {
577 path: Path::from(vec!["d".into()]),
578 content: "value-d".into(),
579 rules: vec![
580 VisitedRule {
581 rule_index: 0,
582 is_excluded: false
583 },
584 VisitedRule {
585 rule_index: 1,
586 is_excluded: false
587 },
588 VisitedRule {
589 rule_index: 2,
590 is_excluded: false
591 }
592 ]
593 }
594 ],
595 },
596 );
597 }
598
599 #[test]
600 fn test_exclusive_scopes_array() {
601 let ruleset = ScopedRuleSet::new(&[
603 Scope::exclude(vec![Path::from(vec![0.into()])]),
604 Scope::exclude(vec![Path::from(vec![1.into(), 0.into()])]),
605 Scope::exclude(vec![Path::from(vec![2.into(), 0.into()])]),
606 ]);
607
608 let mut event = SimpleEvent::List(vec![
609 SimpleEvent::String("value-0".into()),
610 SimpleEvent::String("value-1".into()),
611 SimpleEvent::List(vec![SimpleEvent::String("value-2-0".into())]),
612 SimpleEvent::String("value-3".into()),
613 ]);
614
615 let paths = visit_event(&mut event, &ruleset);
616
617 assert_eq!(
618 paths,
619 Visited {
620 paths: vec![
621 VisitedPath {
622 path: Path::from(vec![0.into()]),
623 content: "value-0".into(),
624 rules: vec![
625 VisitedRule {
626 rule_index: 0,
627 is_excluded: true
628 },
629 VisitedRule {
630 rule_index: 1,
631 is_excluded: false
632 },
633 VisitedRule {
634 rule_index: 2,
635 is_excluded: false
636 }
637 ]
638 },
639 VisitedPath {
640 path: Path::from(vec![1.into()]),
641 content: "value-1".into(),
642 rules: vec![
643 VisitedRule {
644 rule_index: 0,
645 is_excluded: false
646 },
647 VisitedRule {
648 rule_index: 1,
649 is_excluded: false
650 },
651 VisitedRule {
652 rule_index: 2,
653 is_excluded: false
654 }
655 ]
656 },
657 VisitedPath {
658 path: Path::from(vec![2.into(), 0.into()]),
659 content: "value-2-0".into(),
660 rules: vec![
661 VisitedRule {
662 rule_index: 0,
663 is_excluded: false
664 },
665 VisitedRule {
666 rule_index: 1,
667 is_excluded: false
668 },
669 VisitedRule {
670 rule_index: 2,
671 is_excluded: true
672 }
673 ]
674 },
675 VisitedPath {
676 path: Path::from(vec![3.into()]),
677 content: "value-3".into(),
678 rules: vec![
679 VisitedRule {
680 rule_index: 0,
681 is_excluded: false
682 },
683 VisitedRule {
684 rule_index: 1,
685 is_excluded: false
686 },
687 VisitedRule {
688 rule_index: 2,
689 is_excluded: false
690 }
691 ]
692 }
693 ],
694 }
695 );
696 }
697
698 #[test]
699 fn test_include_and_exclude() {
700 let ruleset = ScopedRuleSet::new(&[Scope::include_and_exclude(
703 vec![Path::from(vec![2.into()])],
704 vec![Path::from(vec![2.into(), 0.into()])],
705 )]);
706
707 let mut event = SimpleEvent::List(vec![
708 SimpleEvent::String("value-0".into()),
709 SimpleEvent::String("value-1".into()),
710 SimpleEvent::List(vec![
711 SimpleEvent::String("value-2-0".into()),
712 SimpleEvent::String("value-2-1".into()),
713 ]),
714 SimpleEvent::String("value-3".into()),
715 ]);
716
717 let paths = visit_event(&mut event, &ruleset);
718
719 assert_eq!(
720 paths,
721 Visited {
722 paths: vec![
723 VisitedPath {
724 path: Path::from(vec![0.into()]),
725 content: "value-0".into(),
726 rules: vec![]
727 },
728 VisitedPath {
729 path: Path::from(vec![1.into()]),
730 content: "value-1".into(),
731 rules: vec![]
732 },
733 VisitedPath {
734 path: Path::from(vec![2.into(), 0.into()]),
735 content: "value-2-0".into(),
736 rules: vec![VisitedRule {
737 rule_index: 0,
738 is_excluded: true
739 }]
740 },
741 VisitedPath {
742 path: Path::from(vec![2.into(), 1.into()]),
743 content: "value-2-1".into(),
744 rules: vec![VisitedRule {
745 rule_index: 0,
746 is_excluded: false
747 }]
748 },
749 VisitedPath {
750 path: Path::from(vec![3.into()]),
751 content: "value-3".into(),
752 rules: vec![]
753 },
754 ]
755 }
756 );
757 }
758
759 #[test]
760 fn test_include_and_exclude_priority() {
761 let ruleset = ScopedRuleSet::new(&[Scope::include_and_exclude(
764 vec![Path::from(vec![1.into(), 0.into()])],
766 vec![Path::from(vec![1.into()])],
767 )]);
768
769 let mut event = SimpleEvent::List(vec![
770 SimpleEvent::String("value-0".into()),
771 SimpleEvent::List(vec![
772 SimpleEvent::String("value-1-0".into()),
773 SimpleEvent::String("value-1-1".into()),
774 ]),
775 ]);
776
777 let paths = visit_event(&mut event, &ruleset);
778
779 assert_eq!(
780 paths,
781 Visited {
782 paths: vec![
783 VisitedPath {
784 path: Path::from(vec![0.into()]),
785 content: "value-0".into(),
786 rules: vec![]
787 },
788 VisitedPath {
789 path: Path::from(vec![1.into(), 0.into()]),
790 content: "value-1-0".into(),
791 rules: vec![VisitedRule {
792 rule_index: 0,
793 is_excluded: true
794 }]
795 },
796 VisitedPath {
797 path: Path::from(vec![1.into(), 1.into()]),
798 content: "value-1-1".into(),
799 rules: vec![]
800 }
801 ],
802 }
803 );
804 }
805
806 #[test]
807 fn test_include_same_change_multiple_times() {
808 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![
811 Path::from(vec!["a".into()]),
812 Path::from(vec!["a".into(), "b".into()]),
813 ])]);
814
815 let mut event = SimpleEvent::Map(
816 [
817 (
818 "a".into(),
819 SimpleEvent::Map(
820 [
821 ("b".into(), SimpleEvent::String("value-ab".into())),
822 ("c".into(), SimpleEvent::String("value-ac".into())),
823 ]
824 .into(),
825 ),
826 ),
827 ("d".into(), SimpleEvent::String("value-d".into())),
828 ]
829 .into(),
830 );
831
832 let paths = visit_event(&mut event, &ruleset);
833
834 assert_eq!(
835 paths,
836 Visited {
837 paths: vec![
838 VisitedPath {
839 path: Path::from(vec!["a".into(), "b".into()]),
840 content: "value-ab".into(),
841 rules: vec![VisitedRule {
842 rule_index: 0,
843 is_excluded: false
844 }]
845 },
846 VisitedPath {
847 path: Path::from(vec!["a".into(), "c".into()]),
848 content: "value-ac".into(),
849 rules: vec![VisitedRule {
850 rule_index: 0,
851 is_excluded: false
852 }]
853 },
854 VisitedPath {
855 path: Path::from(vec!["d".into()]),
856 content: "value-d".into(),
857 rules: vec![]
858 }
859 ],
860 }
861 );
862 }
863
864 #[test]
865 fn test_include_root_multiple_times() {
866 let ruleset =
868 ScopedRuleSet::new(&[Scope::include(vec![Path::from(vec![]), Path::from(vec![])])]);
869
870 let mut event = SimpleEvent::Map(
871 [
872 (
873 "a".into(),
874 SimpleEvent::Map(
875 [
876 ("b".into(), SimpleEvent::String("value-ab".into())),
877 ("c".into(), SimpleEvent::String("value-ac".into())),
878 ]
879 .into(),
880 ),
881 ),
882 ("d".into(), SimpleEvent::String("value-d".into())),
883 ]
884 .into(),
885 );
886
887 let paths = visit_event(&mut event, &ruleset);
888
889 assert_eq!(
890 paths,
891 Visited {
892 paths: vec![
893 VisitedPath {
894 path: Path::from(vec!["a".into(), "b".into()]),
895 content: "value-ab".into(),
896 rules: vec![VisitedRule {
897 rule_index: 0,
898 is_excluded: false
899 }]
900 },
901 VisitedPath {
902 path: Path::from(vec!["a".into(), "c".into()]),
903 content: "value-ac".into(),
904 rules: vec![VisitedRule {
905 rule_index: 0,
906 is_excluded: false
907 }]
908 },
909 VisitedPath {
910 path: Path::from(vec!["d".into()]),
911 content: "value-d".into(),
912 rules: vec![VisitedRule {
913 rule_index: 0,
914 is_excluded: false
915 }]
916 }
917 ],
918 }
919 );
920 }
921
922 #[test]
923 fn test_include_same_change_multiple_times_reversed() {
924 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![
929 Path::from(vec!["a".into(), "b".into()]),
930 Path::from(vec!["a".into()]),
931 ])]);
932
933 let mut event = SimpleEvent::Map(
934 [
935 (
936 "a".into(),
937 SimpleEvent::Map(
938 [
939 ("b".into(), SimpleEvent::String("value-ab".into())),
940 ("c".into(), SimpleEvent::String("value-ac".into())),
941 ]
942 .into(),
943 ),
944 ),
945 ("d".into(), SimpleEvent::String("value-d".into())),
946 ]
947 .into(),
948 );
949
950 let paths = visit_event(&mut event, &ruleset);
951
952 assert_eq!(
953 paths,
954 Visited {
955 paths: vec![
956 VisitedPath {
957 path: Path::from(vec!["a".into(), "b".into()]),
958 content: "value-ab".into(),
959 rules: vec![VisitedRule {
960 rule_index: 0,
961 is_excluded: false
962 }]
963 },
964 VisitedPath {
965 path: Path::from(vec!["a".into(), "c".into()]),
966 content: "value-ac".into(),
967 rules: vec![VisitedRule {
968 rule_index: 0,
969 is_excluded: false
970 }]
971 },
972 VisitedPath {
973 path: Path::from(vec!["d".into()]),
974 content: "value-d".into(),
975 rules: vec![]
976 }
977 ],
978 }
979 );
980 }
981
982 #[test]
983 fn test_fields_should_act_as_wildcard_on_lists() {
984 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![Path::from(vec![
985 "a".into(),
986 "b".into(),
987 ])])])
988 .with_implicit_index_wildcards(true);
989
990 let mut event = SimpleEvent::Map(
991 [(
992 "a".into(),
993 SimpleEvent::List(vec![SimpleEvent::Map(
994 [("b".into(), SimpleEvent::String("value-a-0-b".to_string()))].into(),
995 )]),
996 )]
997 .into(),
998 );
999
1000 let paths = visit_event(&mut event, &ruleset);
1001
1002 assert_eq!(
1003 paths,
1004 Visited {
1005 paths: vec![VisitedPath {
1006 path: Path::from(vec!["a".into(), 0.into(), "b".into()]),
1007 content: "value-a-0-b".into(),
1008 rules: vec![VisitedRule {
1010 rule_index: 0,
1011 is_excluded: false
1012 }]
1013 },],
1014 }
1015 );
1016 }
1017
1018 #[test]
1019 fn test_exclude_implicit_wildcard_path() {
1020 let ab_path = Path::from(vec!["a".into(), "b".into()]);
1024 let ruleset = ScopedRuleSet::new(&[Scope::include_and_exclude(
1025 vec![ab_path.clone()],
1026 vec![ab_path],
1027 )])
1028 .with_implicit_index_wildcards(true);
1029
1030 let mut event = SimpleEvent::Map(
1031 [(
1032 "a".into(),
1033 SimpleEvent::List(vec![SimpleEvent::Map(
1034 [("b".into(), SimpleEvent::String("value-a-0-b".to_string()))].into(),
1035 )]),
1036 )]
1037 .into(),
1038 );
1039
1040 let paths = visit_event(&mut event, &ruleset);
1041
1042 assert_eq!(
1043 paths,
1044 Visited {
1045 paths: vec![VisitedPath {
1046 path: Path::from(vec!["a".into(), 0.into(), "b".into()]),
1047 content: "value-a-0-b".into(),
1048 rules: vec![VisitedRule {
1050 rule_index: 0,
1051 is_excluded: true
1052 }]
1053 },],
1054 }
1055 );
1056 }
1057
1058 #[test]
1059 fn test_included_scope_both_implicit_and_explicit_index() {
1060 let a_0_c_path = Path::from(vec!["a".into(), 0.into(), "c".into()]);
1061 let ab_path = Path::from(vec!["a".into(), "b".into()]);
1062 let a_1_d_path = Path::from(vec!["a".into(), 1.into(), "d".into()]);
1063
1064 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![a_0_c_path, ab_path, a_1_d_path])])
1065 .with_implicit_index_wildcards(true);
1066
1067 let mut event = SimpleEvent::Map(
1068 [(
1069 "a".into(),
1070 SimpleEvent::List(vec![
1071 SimpleEvent::Map(
1072 [
1073 ("b".into(), SimpleEvent::String("value-a-0-b".to_string())),
1074 ("c".into(), SimpleEvent::String("value-a-0-c".to_string())),
1075 ("d".into(), SimpleEvent::String("value-a-0-d".to_string())),
1076 ]
1077 .into(),
1078 ),
1079 SimpleEvent::Map(
1080 [
1081 ("b".into(), SimpleEvent::String("value-a-1-b".to_string())),
1082 ("c".into(), SimpleEvent::String("value-a-1-c".to_string())),
1083 ("d".into(), SimpleEvent::String("value-a-1-d".to_string())),
1084 ]
1085 .into(),
1086 ),
1087 ]),
1088 )]
1089 .into(),
1090 );
1091
1092 let paths = visit_event(&mut event, &ruleset);
1093
1094 assert_eq!(
1095 paths,
1096 Visited {
1097 paths: vec![
1098 VisitedPath {
1099 path: Path::from(vec!["a".into(), 0.into(), "b".into()]),
1100 content: "value-a-0-b".into(),
1101 rules: vec![VisitedRule {
1102 rule_index: 0,
1103 is_excluded: false
1104 }]
1105 },
1106 VisitedPath {
1107 path: Path::from(vec!["a".into(), 0.into(), "c".into()]),
1108 content: "value-a-0-c".into(),
1109 rules: vec![VisitedRule {
1110 rule_index: 0,
1111 is_excluded: false
1112 }]
1113 },
1114 VisitedPath {
1115 path: Path::from(vec!["a".into(), 0.into(), "d".into()]),
1116 content: "value-a-0-d".into(),
1117 rules: vec![]
1118 },
1119 VisitedPath {
1120 path: Path::from(vec!["a".into(), 1.into(), "b".into()]),
1121 content: "value-a-1-b".into(),
1122 rules: vec![VisitedRule {
1123 rule_index: 0,
1124 is_excluded: false
1125 }]
1126 },
1127 VisitedPath {
1128 path: Path::from(vec!["a".into(), 1.into(), "c".into()]),
1129 content: "value-a-1-c".into(),
1130 rules: vec![]
1131 },
1132 VisitedPath {
1133 path: Path::from(vec!["a".into(), 1.into(), "d".into()]),
1134 content: "value-a-1-d".into(),
1135 rules: vec![VisitedRule {
1136 rule_index: 0,
1137 is_excluded: false
1138 }]
1139 },
1140 ],
1141 }
1142 );
1143 }
1144
1145 #[test]
1146 fn test_duplicate_rules_are_filtered_out() {
1147 let a_b_path = Path::from(vec!["a".into(), "b".into()]);
1152 let a_0_b_path = Path::from(vec!["a".into(), 0.into(), "b".into()]);
1153
1154 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![a_b_path, a_0_b_path])])
1155 .with_implicit_index_wildcards(true);
1156
1157 let mut event = SimpleEvent::Map(
1158 [(
1159 "a".into(),
1160 SimpleEvent::List(vec![SimpleEvent::Map(
1161 [("b".into(), SimpleEvent::String("value-a-0-b".to_string()))].into(),
1162 )]),
1163 )]
1164 .into(),
1165 );
1166
1167 let paths = visit_event(&mut event, &ruleset);
1168
1169 assert_eq!(
1170 paths,
1171 Visited {
1172 paths: vec![VisitedPath {
1173 path: Path::from(vec!["a".into(), 0.into(), "b".into()]),
1174 content: "value-a-0-b".into(),
1175 rules: vec![VisitedRule {
1176 rule_index: 0,
1177 is_excluded: false
1178 }]
1179 },],
1180 }
1181 );
1182 }
1183
1184 #[test]
1185 fn test_deeply_nested_implicit_wildcard_index() {
1186 let a_b_path = Path::from(vec!["a".into(), "b".into()]);
1189 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![a_b_path])])
1190 .with_implicit_index_wildcards(true);
1191
1192 let mut event = SimpleEvent::Map(
1193 [(
1194 "a".into(),
1195 SimpleEvent::List(vec![SimpleEvent::List(vec![SimpleEvent::Map(
1196 [("b".into(), SimpleEvent::String("value-a-0-0-b".to_string()))].into(),
1197 )])]),
1198 )]
1199 .into(),
1200 );
1201
1202 let paths = visit_event(&mut event, &ruleset);
1203
1204 assert_eq!(
1205 paths,
1206 Visited {
1207 paths: vec![VisitedPath {
1208 path: Path::from(vec!["a".into(), 0.into(), 0.into(), "b".into()]),
1209 content: "value-a-0-0-b".into(),
1210 rules: vec![VisitedRule {
1211 rule_index: 0,
1212 is_excluded: false
1213 }]
1214 },],
1215 }
1216 );
1217 }
1218
1219 #[test]
1220 fn test_implicit_index_wildcard_is_disabled_by_default() {
1221 let ruleset = ScopedRuleSet::new(&[Scope::include(vec![Path::from(vec![
1222 "a".into(),
1223 "b".into(),
1224 ])])]);
1225
1226 let mut event = SimpleEvent::Map(
1227 [(
1228 "a".into(),
1229 SimpleEvent::List(vec![SimpleEvent::Map(
1230 [("b".into(), SimpleEvent::String("value-a-0-b".to_string()))].into(),
1231 )]),
1232 )]
1233 .into(),
1234 );
1235
1236 let paths = visit_event(&mut event, &ruleset);
1237
1238 assert_eq!(
1239 paths,
1240 Visited {
1241 paths: vec![VisitedPath {
1242 path: Path::from(vec!["a".into(), 0.into(), "b".into()]),
1243 content: "value-a-0-b".into(),
1244 rules: vec![]
1246 },],
1247 }
1248 );
1249 }
1250}