memscope_rs/analysis/generic/
utils.rs1use super::types::{ConstraintType, GenericConstraint};
2
3pub fn extract_constraints(type_name: &str) -> Vec<GenericConstraint> {
4 let mut constraints = Vec::new();
5
6 if is_collection_type(type_name) {
7 constraints.push(GenericConstraint {
8 parameter_name: "T".to_string(),
9 constraint_type: ConstraintType::Sized,
10 description: "Type must be Sized for standard collections".to_string(),
11 });
12 }
13
14 if is_smart_pointer_type(type_name) {
15 constraints.push(GenericConstraint {
16 parameter_name: "T".to_string(),
17 constraint_type: ConstraintType::Sized,
18 description: "Type must be Sized for smart pointers".to_string(),
19 });
20 }
21
22 if is_thread_safe_type(type_name) {
23 constraints.push(GenericConstraint {
24 parameter_name: "T".to_string(),
25 constraint_type: ConstraintType::Send,
26 description: "Type must be Send for thread-safe containers".to_string(),
27 });
28 }
29
30 if is_sync_required_type(type_name) {
31 constraints.push(GenericConstraint {
32 parameter_name: "T".to_string(),
33 constraint_type: ConstraintType::Sync,
34 description: "Type must be Sync for shared concurrent access".to_string(),
35 });
36 }
37
38 constraints
39}
40
41fn is_collection_type(type_name: &str) -> bool {
42 let collection_patterns = [
43 r"\bVec<",
44 r"\bVecDeque<",
45 r"\bLinkedList<",
46 r"\bHashMap<",
47 r"\bBTreeMap<",
48 r"\bHashSet<",
49 r"\bBTreeSet<",
50 r"\bBinaryHeap<",
51 ];
52
53 collection_patterns.iter().any(|pattern| {
54 regex::Regex::new(pattern)
55 .map(|re| re.is_match(type_name))
56 .unwrap_or(false)
57 })
58}
59
60fn is_smart_pointer_type(type_name: &str) -> bool {
61 let smart_pointer_patterns = [r"\bBox<", r"\bRc<", r"\bArc<", r"\bWeak<"];
62
63 smart_pointer_patterns.iter().any(|pattern| {
64 regex::Regex::new(pattern)
65 .map(|re| re.is_match(type_name))
66 .unwrap_or(false)
67 })
68}
69
70fn is_thread_safe_type(type_name: &str) -> bool {
71 let thread_safe_patterns = [
72 r"\bMutex<",
73 r"\bRwLock<",
74 r"\bmpsc::",
75 r"\bSender<",
76 r"\bReceiver<",
77 ];
78
79 thread_safe_patterns.iter().any(|pattern| {
80 regex::Regex::new(pattern)
81 .map(|re| re.is_match(type_name))
82 .unwrap_or(false)
83 })
84}
85
86fn is_sync_required_type(type_name: &str) -> bool {
87 let sync_required_patterns = [r"\bArc<", r"&\s*Mutex<", r"&\s*RwLock<"];
88
89 sync_required_patterns.iter().any(|pattern| {
90 regex::Regex::new(pattern)
91 .map(|re| re.is_match(type_name))
92 .unwrap_or(false)
93 })
94}
95
96pub fn parse_generic_parameters(type_name: &str) -> (String, Vec<String>) {
97 if let Some(start) = type_name.find('<') {
98 if let Some(end) = type_name.rfind('>') {
99 let base_type = type_name[..start].to_string();
100 let params_str = &type_name[start + 1..end];
101
102 let params: Vec<String> = params_str
103 .split(',')
104 .map(|s| s.trim().to_string())
105 .filter(|s| !s.is_empty())
106 .collect();
107
108 return (base_type, params);
109 }
110 }
111
112 (type_name.to_string(), Vec::new())
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
122 fn test_extract_constraints_vec() {
123 let constraints = extract_constraints("Vec<i32>");
124 assert!(!constraints.is_empty(), "Vec should have constraints");
125 assert!(
126 constraints
127 .iter()
128 .any(|c| matches!(c.constraint_type, ConstraintType::Sized)),
129 "Vec should have Sized constraint"
130 );
131 }
132
133 #[test]
136 fn test_extract_constraints_hashmap() {
137 let constraints = extract_constraints("HashMap<String, i32>");
138 assert!(!constraints.is_empty(), "HashMap should have constraints");
139 }
140
141 #[test]
144 fn test_extract_constraints_box() {
145 let constraints = extract_constraints("Box<MyStruct>");
146 assert!(!constraints.is_empty(), "Box should have constraints");
147 assert!(
148 constraints
149 .iter()
150 .any(|c| matches!(c.constraint_type, ConstraintType::Sized)),
151 "Box should have Sized constraint"
152 );
153 }
154
155 #[test]
158 fn test_extract_constraints_arc() {
159 let constraints = extract_constraints("Arc<MyStruct>");
160 assert!(!constraints.is_empty(), "Arc should have constraints");
161 assert!(
162 constraints
163 .iter()
164 .any(|c| matches!(c.constraint_type, ConstraintType::Sync)),
165 "Arc should have Sync constraint"
166 );
167 }
168
169 #[test]
172 fn test_extract_constraints_mutex() {
173 let constraints = extract_constraints("Mutex<MyData>");
174 assert!(!constraints.is_empty(), "Mutex should have constraints");
175 assert!(
176 constraints
177 .iter()
178 .any(|c| matches!(c.constraint_type, ConstraintType::Send)),
179 "Mutex should have Send constraint"
180 );
181 }
182
183 #[test]
186 fn test_extract_constraints_non_generic() {
187 let constraints = extract_constraints("i32");
188 assert!(
189 constraints.is_empty(),
190 "i32 should have no generic constraints"
191 );
192 }
193
194 #[test]
197 fn test_extract_constraints_empty() {
198 let constraints = extract_constraints("");
199 assert!(
200 constraints.is_empty(),
201 "Empty string should have no constraints"
202 );
203 }
204
205 #[test]
208 fn test_parse_generic_parameters_vec() {
209 let (base, params) = parse_generic_parameters("Vec<i32>");
210 assert_eq!(base, "Vec", "Base type should be Vec");
211 assert_eq!(params, vec!["i32"], "Should have one parameter");
212 }
213
214 #[test]
217 fn test_parse_generic_parameters_hashmap() {
218 let (base, params) = parse_generic_parameters("HashMap<String, i32>");
219 assert_eq!(base, "HashMap", "Base type should be HashMap");
220 assert_eq!(params, vec!["String", "i32"], "Should have two parameters");
221 }
222
223 #[test]
226 fn test_parse_generic_parameters_nested() {
227 let (base, params) = parse_generic_parameters("Vec<Vec<i32>>");
228 assert_eq!(base, "Vec", "Base type should be Vec");
229 assert_eq!(params.len(), 1, "Should have one parameter");
230 assert!(params[0].contains("Vec"), "Parameter should contain Vec");
231 }
232
233 #[test]
236 fn test_parse_generic_parameters_non_generic() {
237 let (base, params) = parse_generic_parameters("String");
238 assert_eq!(base, "String", "Base should be original string");
239 assert!(params.is_empty(), "Should have no parameters");
240 }
241
242 #[test]
245 fn test_parse_generic_parameters_empty() {
246 let (base, params) = parse_generic_parameters("");
247 assert_eq!(base, "", "Base should be empty");
248 assert!(params.is_empty(), "Should have no parameters");
249 }
250
251 #[test]
254 fn test_parse_generic_parameters_malformed() {
255 let (base, params) = parse_generic_parameters("Vec<i32");
256 assert_eq!(base, "Vec<i32", "Malformed should return original");
257 assert!(params.is_empty(), "Should have no parameters for malformed");
258 }
259
260 #[test]
263 fn test_parse_generic_parameters_whitespace() {
264 let (base, params) = parse_generic_parameters("HashMap< String , i32 >");
265 assert_eq!(base, "HashMap", "Base should be HashMap");
266 assert_eq!(
267 params,
268 vec!["String", "i32"],
269 "Parameters should be trimmed"
270 );
271 }
272
273 #[test]
276 fn test_extract_constraints_rc() {
277 let constraints = extract_constraints("Rc<MyStruct>");
278 assert!(!constraints.is_empty(), "Rc should have constraints");
279 }
280
281 #[test]
284 fn test_extract_constraints_rwlock() {
285 let constraints = extract_constraints("RwLock<Data>");
286 assert!(!constraints.is_empty(), "RwLock should have constraints");
287 }
288
289 #[test]
292 fn test_extract_constraints_complex_nested() {
293 let constraints = extract_constraints("Arc<Mutex<Vec<HashMap<String, i32>>>>");
294 assert!(
295 !constraints.is_empty(),
296 "Complex nested type should have constraints"
297 );
298 assert!(
299 constraints
300 .iter()
301 .any(|c| matches!(c.constraint_type, ConstraintType::Sync)),
302 "Arc should contribute Sync constraint"
303 );
304 }
305
306 #[test]
309 fn test_extract_constraints_vecdeque() {
310 let constraints = extract_constraints("VecDeque<i32>");
311 assert!(!constraints.is_empty(), "VecDeque should have constraints");
312 }
313
314 #[test]
317 fn test_extract_constraints_btreemap() {
318 let constraints = extract_constraints("BTreeMap<String, i32>");
319 assert!(!constraints.is_empty(), "BTreeMap should have constraints");
320 }
321
322 #[test]
325 fn test_extract_constraints_binaryheap() {
326 let constraints = extract_constraints("BinaryHeap<i32>");
327 assert!(
328 !constraints.is_empty(),
329 "BinaryHeap should have constraints"
330 );
331 }
332
333 #[test]
336 fn test_extract_constraints_linkedlist() {
337 let constraints = extract_constraints("LinkedList<i32>");
338 assert!(
339 !constraints.is_empty(),
340 "LinkedList should have constraints"
341 );
342 }
343
344 #[test]
347 fn test_extract_constraints_weak() {
348 let constraints = extract_constraints("Weak<MyStruct>");
349 assert!(!constraints.is_empty(), "Weak should have constraints");
350 }
351
352 #[test]
355 fn test_extract_constraints_channel() {
356 let sender_constraints = extract_constraints("mpsc::Sender<Message>");
357 assert!(
358 !sender_constraints.is_empty(),
359 "Sender should have constraints"
360 );
361
362 let receiver_constraints = extract_constraints("mpsc::Receiver<Message>");
363 assert!(
364 !receiver_constraints.is_empty(),
365 "Receiver should have constraints"
366 );
367 }
368}