tramex_tools/interface/association/
matcher.rs1use super::relation::AssociationStatus;
4use super::rules::{AssociationRule, SearchDirection};
5use crate::data::Trace;
6use crate::interface::layer::Layer;
7
8pub struct TraceMatcher;
12
13impl TraceMatcher {
14 pub fn find_relative(
24 source_index: usize,
25 events: &[Trace],
26 rule: &dyn AssociationRule,
27 source_layer: &Layer,
28 target_layer: &Layer,
29 ) -> AssociationStatus {
30 let source = match events.get(source_index) {
31 Some(t) => t,
32 None => return AssociationStatus::NotFound,
33 };
34
35 if &source.layer != source_layer {
37 return AssociationStatus::NotApplicable;
38 }
39
40 let direction = rule.preferred_direction(source);
41
42 match direction {
44 SearchDirection::BackwardFirst => {
45 if let Some(idx) = Self::search_backward(source_index, events, source, rule, target_layer) {
46 return AssociationStatus::Found(vec![idx]);
47 }
48 if let Some(idx) = Self::search_forward(source_index, events, source, rule, target_layer) {
49 return AssociationStatus::Found(vec![idx]);
50 }
51 }
52 SearchDirection::ForwardFirst => {
53 if let Some(idx) = Self::search_forward(source_index, events, source, rule, target_layer) {
54 return AssociationStatus::Found(vec![idx]);
55 }
56 if let Some(idx) = Self::search_backward(source_index, events, source, rule, target_layer) {
57 return AssociationStatus::Found(vec![idx]);
58 }
59 }
60 SearchDirection::BackwardOnly => {
61 if let Some(idx) = Self::search_backward(source_index, events, source, rule, target_layer) {
62 return AssociationStatus::Found(vec![idx]);
63 }
64 }
65 SearchDirection::ForwardOnly => {
66 if let Some(idx) = Self::search_forward(source_index, events, source, rule, target_layer) {
67 return AssociationStatus::Found(vec![idx]);
68 }
69 }
70 }
71
72 AssociationStatus::NotFound
73 }
74
75 pub fn find_relative_reverse(source_index: usize, events: &[Trace], rule: &dyn AssociationRule) -> AssociationStatus {
77 Self::find_relative(source_index, events, rule, &rule.target_layer(), &rule.source_layer())
78 }
79
80 fn search_backward(
82 index: usize,
83 events: &[Trace],
84 source: &Trace,
85 rule: &dyn AssociationRule,
86 target_layer: &Layer,
87 ) -> Option<usize> {
88 let window_size = rule.window_size();
89 let start = index.saturating_sub(window_size);
90 for idx in (start..index).rev() {
91 let candidate = &events[idx];
92
93 if &candidate.layer != target_layer {
94 continue;
95 }
96
97 if rule.matches(source, candidate) {
98 return Some(idx);
99 }
100 }
101 None
102 }
103
104 fn search_forward(
106 index: usize,
107 events: &[Trace],
108 source: &Trace,
109 rule: &dyn AssociationRule,
110 target_layer: &Layer,
111 ) -> Option<usize> {
112 let window_size = rule.window_size();
113 let end = (index + window_size + 1).min(events.len());
114 for (idx, candidate) in events.iter().enumerate().take(end).skip(index + 1) {
115 if &candidate.layer != target_layer {
116 continue;
117 }
118
119 if rule.matches(source, candidate) {
120 return Some(idx);
121 }
122 }
123 None
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn test_search_direction_from_direction() {
133 use crate::interface::types::Direction;
134
135 assert_eq!(
136 SearchDirection::from_direction(&Direction::UL),
137 SearchDirection::BackwardFirst
138 );
139 assert_eq!(SearchDirection::from_direction(&Direction::DL), SearchDirection::ForwardFirst);
140 }
141}