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