tramex_tools/interface/association/
relation.rs

1//! Trace relation structures for parent/child associations
2
3/// Status of a trace association attempt
4#[derive(Debug, Clone, Default, PartialEq)]
5pub enum AssociationStatus {
6    /// Association has not been computed yet
7    #[default]
8    NotComputed,
9    /// Association found - contains indices of related traces (can be multiple)
10    Found(Vec<usize>),
11    /// Association was searched but no match found within the window
12    NotFound,
13    /// This trace type does not support associations
14    NotApplicable,
15}
16
17impl AssociationStatus {
18    /// Returns true if the association has been computed (Found, NotFound, or NotApplicable)
19    pub fn is_computed(&self) -> bool {
20        !matches!(self, AssociationStatus::NotComputed)
21    }
22
23    /// Returns the first index if found (for backwards compatibility)
24    pub fn get_index(&self) -> Option<usize> {
25        match self {
26            AssociationStatus::Found(indices) => indices.first().copied(),
27            _ => None,
28        }
29    }
30
31    /// Returns all indices if found
32    pub fn get_indices(&self) -> Option<&Vec<usize>> {
33        match self {
34            AssociationStatus::Found(indices) => Some(indices),
35            _ => None,
36        }
37    }
38
39    /// Add an index to the association
40    /// - If NotComputed or NotFound: becomes Found with single index
41    /// - If Found: appends index if not already present
42    /// - If NotApplicable: no change (returns false)
43    pub fn add_index(&mut self, index: usize) -> bool {
44        match self {
45            AssociationStatus::NotComputed | AssociationStatus::NotFound => {
46                *self = AssociationStatus::Found(vec![index]);
47                true
48            }
49            AssociationStatus::Found(indices) => {
50                if !indices.contains(&index) {
51                    indices.push(index);
52                }
53                true
54            }
55            AssociationStatus::NotApplicable => false,
56        }
57    }
58
59    /// Check if this status contains a specific index
60    pub fn contains(&self, index: usize) -> bool {
61        match self {
62            AssociationStatus::Found(indices) => indices.contains(&index),
63            _ => false,
64        }
65    }
66}
67
68/// Holds the parent/child relationship for a trace
69#[derive(Debug, Clone, Default)]
70pub struct TraceRelation {
71    /// Index of the parent trace (e.g., RRC for a NAS message)
72    pub parent: AssociationStatus,
73    /// Index of the child trace (e.g., NAS for an RRC message with dedicated NAS)
74    pub child: AssociationStatus,
75}
76
77impl TraceRelation {
78    /// Create a new empty relation
79    pub fn new() -> Self {
80        Self::default()
81    }
82
83    /// Check if this trace has a parent
84    pub fn has_parent(&self) -> bool {
85        matches!(self.parent, AssociationStatus::Found(_))
86    }
87
88    /// Check if this trace has a child
89    pub fn has_child(&self) -> bool {
90        matches!(self.child, AssociationStatus::Found(_))
91    }
92
93    /// Get parent index if available
94    pub fn get_parent_index(&self) -> Option<usize> {
95        self.parent.get_index()
96    }
97
98    /// Get parent indices if available
99    pub fn get_parent_indices(&self) -> Option<&Vec<usize>> {
100        self.parent.get_indices()
101    }
102
103    /// Get child index if available
104    pub fn get_child_index(&self) -> Option<usize> {
105        self.child.get_index()
106    }
107
108    /// Get child indices if available
109    pub fn get_child_indices(&self) -> Option<&Vec<usize>> {
110        self.child.get_indices()
111    }
112
113    /// Set parent as found (replaces existing)
114    pub fn set_parent(&mut self, index: usize) {
115        self.parent = AssociationStatus::Found(vec![index]);
116    }
117
118    /// Add a parent index (appends to existing if Found)
119    pub fn add_parent(&mut self, index: usize) -> bool {
120        self.parent.add_index(index)
121    }
122
123    /// Set child as found (replaces existing)
124    pub fn set_child(&mut self, index: usize) {
125        self.child = AssociationStatus::Found(vec![index]);
126    }
127
128    /// Add a child index (appends to existing if Found)
129    pub fn add_child(&mut self, index: usize) -> bool {
130        self.child.add_index(index)
131    }
132
133    /// Mark parent search as completed with no result
134    pub fn set_parent_not_found(&mut self) {
135        self.parent = AssociationStatus::NotFound;
136    }
137
138    /// Mark child search as completed with no result
139    pub fn set_child_not_found(&mut self) {
140        self.child = AssociationStatus::NotFound;
141    }
142
143    /// Mark parent as not applicable for this trace type
144    pub fn set_parent_not_applicable(&mut self) {
145        self.parent = AssociationStatus::NotApplicable;
146    }
147
148    /// Mark child as not applicable for this trace type
149    pub fn set_child_not_applicable(&mut self) {
150        self.child = AssociationStatus::NotApplicable;
151    }
152
153    /// Reset the relation (mark as not computed) - useful when new traces arrive
154    pub fn invalidate(&mut self) {
155        self.parent = AssociationStatus::NotComputed;
156        self.child = AssociationStatus::NotComputed;
157    }
158}