Skip to main content

memscope_rs/analysis/generic/
utils.rs

1use 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    /// Objective: Verify extract_constraints identifies Vec as collection type
120    /// Invariants: Vec<T> should have Sized constraint
121    #[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    /// Objective: Verify extract_constraints identifies HashMap as collection type
134    /// Invariants: HashMap<K, V> should have Sized constraint
135    #[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    /// Objective: Verify extract_constraints identifies Box as smart pointer
142    /// Invariants: Box<T> should have Sized constraint
143    #[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    /// Objective: Verify extract_constraints identifies Arc as thread-safe
156    /// Invariants: Arc<T> should have Send and Sync constraints
157    #[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    /// Objective: Verify extract_constraints identifies Mutex as thread-safe
170    /// Invariants: Mutex<T> should have Send constraint
171    #[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    /// Objective: Verify extract_constraints handles non-generic types
184    /// Invariants: Non-generic types should have no constraints
185    #[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    /// Objective: Verify extract_constraints handles empty string
195    /// Invariants: Empty string should return empty constraints
196    #[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    /// Objective: Verify parse_generic_parameters parses Vec<i32>
206    /// Invariants: Should return base type and single parameter
207    #[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    /// Objective: Verify parse_generic_parameters parses HashMap<K, V>
215    /// Invariants: Should return base type and two parameters
216    #[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    /// Objective: Verify parse_generic_parameters handles nested generics
224    /// Invariants: Should extract outer type and inner as single param
225    #[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    /// Objective: Verify parse_generic_parameters handles non-generic types
234    /// Invariants: Should return original string and empty params
235    #[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    /// Objective: Verify parse_generic_parameters handles empty string
243    /// Invariants: Should return empty string and empty params
244    #[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    /// Objective: Verify parse_generic_parameters handles malformed input
252    /// Invariants: Malformed input should return original string
253    #[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    /// Objective: Verify parse_generic_parameters handles whitespace
261    /// Invariants: Whitespace should be trimmed from parameters
262    #[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    /// Objective: Verify extract_constraints handles Rc
274    /// Invariants: Rc<T> should have Sized constraint
275    #[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    /// Objective: Verify extract_constraints handles RwLock
282    /// Invariants: RwLock<T> should have Send constraint
283    #[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    /// Objective: Verify extract_constraints handles complex nested types
290    /// Invariants: Complex types should be detected correctly
291    #[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    /// Objective: Verify extract_constraints handles VecDeque
307    /// Invariants: VecDeque<T> should have Sized constraint
308    #[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    /// Objective: Verify extract_constraints handles BTreeMap
315    /// Invariants: BTreeMap<K, V> should have Sized constraint
316    #[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    /// Objective: Verify extract_constraints handles BinaryHeap
323    /// Invariants: BinaryHeap<T> should have Sized constraint
324    #[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    /// Objective: Verify extract_constraints handles LinkedList
334    /// Invariants: LinkedList<T> should have Sized constraint
335    #[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    /// Objective: Verify extract_constraints handles Weak
345    /// Invariants: Weak<T> should have Sized constraint
346    #[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    /// Objective: Verify extract_constraints handles mpsc channel types
353    /// Invariants: Sender<T> and Receiver<T> should have Send constraint
354    #[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}