memscope_rs/
utils.rs

1//! Common utility functions shared across modules
2
3/// Format bytes in a human-readable format
4pub fn format_bytes(bytes: usize) -> String {
5    if bytes < 1024 {
6        format!("{bytes}B")
7    } else if bytes < 1024 * 1024 {
8        format!("{:.1}KB", bytes as f64 / 1024.0)
9    } else {
10        format!("{:.1}MB", bytes as f64 / (1024.0 * 1024.0))
11    }
12}
13
14/// Simplify Rust type names for better readability - Enhanced Unknown Type identification
15pub fn simplify_type_name(type_name: &str) -> (String, String) {
16    // Handle empty or explicitly unknown types first
17    if type_name.is_empty() || type_name == "Unknown" {
18        return ("Unknown Type".to_string(), "Unknown".to_string());
19    }
20
21    // Clean up the type name - remove extra whitespace and normalize
22    let clean_type = type_name.trim();
23
24    // Enhanced pattern matching with more comprehensive coverage
25    if clean_type.contains("Vec<") || clean_type.contains("vec::Vec") {
26        let inner = extract_generic_type(clean_type, "Vec");
27        (format!("Vec<{inner}>"), "Collections".to_string())
28    } else if clean_type.contains("String") || clean_type.contains("string::String") {
29        ("String".to_string(), "Basic Types".to_string())
30    } else if clean_type.contains("Box<") || clean_type.contains("boxed::Box") {
31        let inner = extract_generic_type(clean_type, "Box");
32        // Check if the inner type is a collection - if so, categorize as collection
33        if inner.contains("HashMap") || inner.contains("hash_map") {
34            ("HashMap<K,V>".to_string(), "Collections".to_string())
35        } else if inner.contains("BTreeMap") || inner.contains("btree_map") {
36            ("BTreeMap<K,V>".to_string(), "Collections".to_string())
37        } else if inner.contains("BTreeSet") || inner.contains("btree_set") {
38            ("BTreeSet<T>".to_string(), "Collections".to_string())
39        } else if inner.contains("HashSet") || inner.contains("hash_set") {
40            ("HashSet<T>".to_string(), "Collections".to_string())
41        } else if inner.contains("VecDeque") || inner.contains("vec_deque") {
42            ("VecDeque<T>".to_string(), "Collections".to_string())
43        } else if inner.contains("Vec") || inner.contains("vec::Vec") {
44            let vec_inner = extract_generic_type(&inner, "Vec");
45            (format!("Vec<{vec_inner}>"), "Collections".to_string())
46        } else {
47            (format!("Box<{inner}>"), "Smart Pointers".to_string())
48        }
49    } else if clean_type.contains("Rc<") || clean_type.contains("rc::Rc") {
50        let inner = extract_generic_type(clean_type, "Rc");
51        (format!("Rc<{inner}>"), "Smart Pointers".to_string())
52    } else if clean_type.contains("Arc<") || clean_type.contains("sync::Arc") {
53        let inner = extract_generic_type(clean_type, "Arc");
54        (format!("Arc<{inner}>"), "Smart Pointers".to_string())
55    } else if clean_type.contains("HashMap") || clean_type.contains("hash_map") {
56        ("HashMap<K,V>".to_string(), "Collections".to_string())
57    } else if clean_type.contains("BTreeMap") || clean_type.contains("btree_map") {
58        ("BTreeMap<K,V>".to_string(), "Collections".to_string())
59    } else if clean_type.contains("BTreeSet") || clean_type.contains("btree_set") {
60        ("BTreeSet<T>".to_string(), "Collections".to_string())
61    } else if clean_type.contains("HashSet") || clean_type.contains("hash_set") {
62        ("HashSet<T>".to_string(), "Collections".to_string())
63    } else if clean_type.contains("VecDeque") || clean_type.contains("vec_deque") {
64        ("VecDeque<T>".to_string(), "Collections".to_string())
65    } else if clean_type.contains("LinkedList") {
66        ("LinkedList<T>".to_string(), "Collections".to_string())
67    } else if clean_type.contains("&str") || clean_type == "str" {
68        ("&str".to_string(), "Basic Types".to_string())
69    } else if clean_type.contains("CString") || clean_type.contains("CStr") {
70        ("CString".to_string(), "Basic Types".to_string())
71    } else if clean_type.contains("OsString") || clean_type.contains("OsStr") {
72        ("OsString".to_string(), "Basic Types".to_string())
73    } else if clean_type.contains("PathBuf") || clean_type.contains("Path") {
74        ("PathBuf".to_string(), "Basic Types".to_string())
75    } else if clean_type.matches("i32").count() > 0
76        || clean_type.matches("u32").count() > 0
77        || clean_type.matches("i64").count() > 0
78        || clean_type.matches("u64").count() > 0
79        || clean_type.matches("f64").count() > 0
80        || clean_type.matches("f32").count() > 0
81        || clean_type.matches("i8").count() > 0
82        || clean_type.matches("u8").count() > 0
83        || clean_type.matches("i16").count() > 0
84        || clean_type.matches("u16").count() > 0
85        || clean_type.matches("isize").count() > 0
86        || clean_type.matches("usize").count() > 0
87        || clean_type.matches("bool").count() > 0
88        || clean_type.matches("char").count() > 0
89    {
90        let primitive = clean_type.split("::").last().unwrap_or(clean_type);
91        (primitive.to_string(), "Basic Types".to_string())
92    } else if clean_type.contains("[") && clean_type.contains("]") {
93        ("Array".to_string(), "Arrays".to_string())
94    } else if clean_type.starts_with("(") && clean_type.ends_with(")") {
95        ("Tuple".to_string(), "Tuples".to_string())
96    } else if clean_type.contains("Option<") {
97        ("Option<T>".to_string(), "Optionals".to_string())
98    } else if clean_type.contains("Result<") {
99        ("Result<T,E>".to_string(), "Results".to_string())
100    } else if clean_type.contains("Mutex<") || clean_type.contains("RwLock<") {
101        ("Mutex/RwLock".to_string(), "Synchronization".to_string())
102    } else if clean_type.contains("Cell<") || clean_type.contains("RefCell<") {
103        (
104            "Cell/RefCell".to_string(),
105            "Interior Mutability".to_string(),
106        )
107    } else if clean_type.contains("Weak<") {
108        ("Weak<T>".to_string(), "Smart Pointers".to_string())
109    } else if clean_type.starts_with("std::")
110        || clean_type.starts_with("alloc::")
111        || clean_type.starts_with("core::")
112    {
113        let simplified = clean_type.split("::").last().unwrap_or(clean_type);
114        (simplified.to_string(), "Standard Library".to_string())
115    } else if clean_type.contains("::") {
116        // Handle custom types with namespaces
117        let parts: Vec<&str> = clean_type.split("::").collect();
118        if parts.len() >= 2 {
119            let type_part = parts.last().unwrap();
120            // Try to categorize based on common patterns
121            if type_part.ends_with("Error") || type_part.contains("Err") {
122                (type_part.to_string(), "Error Types".to_string())
123            } else if type_part.ends_with("Config") || type_part.ends_with("Settings") {
124                (type_part.to_string(), "Configuration".to_string())
125            } else if type_part.ends_with("Builder") {
126                (type_part.to_string(), "Builders".to_string())
127            } else {
128                (type_part.to_string(), "Custom Types".to_string())
129            }
130        } else {
131            (clean_type.to_string(), "Custom Types".to_string())
132        }
133    } else {
134        // For simple type names without namespace
135        if clean_type.len() > 0 {
136            (clean_type.to_string(), "Custom Types".to_string())
137        } else {
138            ("Unknown Type".to_string(), "Unknown".to_string())
139        }
140    }
141}
142
143/// Extract generic type parameter for display
144pub fn extract_generic_type(type_name: &str, container: &str) -> String {
145    if let Some(start) = type_name.find(&format!("{container}<")) {
146        let start = start + container.len() + 1;
147        if let Some(end) = type_name[start..].rfind('>') {
148            let inner = &type_name[start..start + end];
149            // Simplify the inner type too
150            return inner.split("::").last().unwrap_or(inner).to_string();
151        }
152    }
153    "?".to_string()
154}
155
156/// Get a simplified type name for display
157pub fn get_simple_type(type_name: &str) -> String {
158    if type_name.contains("String") {
159        "String".to_string()
160    } else if type_name.contains("Vec") {
161        "Vec".to_string()
162    } else if type_name.contains("Box") {
163        "Box".to_string()
164    } else if type_name.contains("Rc") {
165        "Rc".to_string()
166    } else if type_name.contains("Arc") {
167        "Arc".to_string()
168    } else if type_name.contains("HashMap") {
169        "HashMap".to_string()
170    } else {
171        type_name
172            .split("::")
173            .last()
174            .unwrap_or("Unknown")
175            .to_string()
176    }
177}
178
179/// Get color for category - Enhanced with new categories
180pub fn get_category_color(category: &str) -> String {
181    match category {
182        "Collections" => "#3498db".to_string(),               // Blue
183        "Basic Types" => "#27ae60".to_string(),               // Green for Basic Types
184        "Strings" => "#27ae60".to_string(),                   // Green (legacy support)
185        "Text" => "#27ae60".to_string(),                      // Green (legacy support)
186        "Smart Pointers" => "#e74c3c".to_string(),            // Red
187        "Reference Counted" => "#f39c12".to_string(),         // Orange
188        "Thread-Safe Shared" => "#9b59b6".to_string(),        // Purple
189        "Primitives" => "#1abc9c".to_string(),                // Teal
190        "Arrays" => "#34495e".to_string(),                    // Dark Gray
191        "Tuples" => "#16a085".to_string(),                    // Dark Teal
192        "Optionals" => "#8e44ad".to_string(),                 // Dark Purple
193        "Results" => "#d35400".to_string(),                   // Dark Orange
194        "Standard Library" => "#2980b9".to_string(),          // Dark Blue
195        "Custom Types" => "#c0392b".to_string(),              // Dark Red
196        "Synchronization" => "#e67e22".to_string(),           // Orange
197        "Interior Mutability" => "#95a5a6".to_string(),       // Light Gray
198        "Error Types" => "#e74c3c".to_string(),               // Red
199        "Configuration" => "#3498db".to_string(),             // Blue
200        "Builders" => "#9b59b6".to_string(),                  // Purple
201        "Runtime/System Allocation" => "#bdc3c7".to_string(), // Light Gray for system allocations
202        "Unknown" => "#bdc3c7".to_string(),                   // Light Gray (legacy support)
203        _ => "#7f8c8d".to_string(),                           // Medium Gray for other unknowns
204    }
205}
206
207/// Get type-specific gradient colors for enhanced visualization
208pub fn get_type_gradient_colors(type_name: &str) -> (&'static str, &'static str) {
209    match type_name {
210        "String" => ("#00BCD4", "#00ACC1"),  // Teal gradient
211        "Vec" => ("#2196F3", "#1976D2"),     // Blue gradient
212        "Box" => ("#F44336", "#D32F2F"),     // Red gradient
213        "HashMap" => ("#4CAF50", "#388E3C"), // Green gradient
214        "Rc" => ("#FF9800", "#F57C00"),      // Orange gradient
215        "Arc" => ("#9C27B0", "#7B1FA2"),     // Purple gradient
216        _ => ("#607D8B", "#455A64"),         // Blue-gray gradient for custom types
217    }
218}
219
220/// Get color based on type for consistent visualization
221pub fn get_type_color(type_name: &str) -> &'static str {
222    match type_name {
223        "String" => "#2ecc71",
224        "Vec" => "#3498db",
225        "Box" => "#e74c3c",
226        "HashMap" => "#f39c12",
227        "Rc" => "#9b59b6",
228        "Arc" => "#1abc9c",
229        _ => "#95a5a6",
230    }
231}
232
233/// Enhanced type hierarchy classification for treemap visualization
234#[derive(Debug, Clone)]
235pub struct TypeHierarchy {
236    /// Major category: Collections, Strings, Smart Pointers, etc.
237    pub major_category: String,
238    /// Sub category: Maps, Sequences, Owned, Shared, etc.
239    pub sub_category: String,
240    /// Specific type: HashMap, Vec, Box, etc.
241    pub specific_type: String,
242    /// Full type name: HashMap<String, i32>
243    pub full_type: String,
244}
245
246/// Get comprehensive type hierarchy for treemap visualization
247pub fn get_type_category_hierarchy(type_name: &str) -> TypeHierarchy {
248    // Handle empty or unknown types first
249    if type_name.is_empty() || type_name == "Unknown" {
250        return TypeHierarchy {
251            major_category: "Unknown".to_string(),
252            sub_category: "Unidentified".to_string(),
253            specific_type: "Unknown Type".to_string(),
254            full_type: type_name.to_string(),
255        };
256    }
257
258    // Collections
259    if type_name.contains("HashMap") || type_name.contains("hash::map") {
260        let inner = extract_generic_params(type_name, "HashMap");
261        TypeHierarchy {
262            major_category: "Collections".to_string(),
263            sub_category: "Maps".to_string(),
264            specific_type: "HashMap".to_string(),
265            full_type: if inner.is_empty() {
266                "HashMap".to_string()
267            } else {
268                format!("HashMap<{}>", inner)
269            },
270        }
271    } else if type_name.contains("BTreeMap") || type_name.contains("btree::map") {
272        let inner = extract_generic_params(type_name, "BTreeMap");
273        TypeHierarchy {
274            major_category: "Collections".to_string(),
275            sub_category: "Maps".to_string(),
276            specific_type: "BTreeMap".to_string(),
277            full_type: if inner.is_empty() {
278                "BTreeMap".to_string()
279            } else {
280                format!("BTreeMap<{}>", inner)
281            },
282        }
283    } else if type_name.contains("HashSet") || type_name.contains("hash::set") {
284        let inner = extract_generic_params(type_name, "HashSet");
285        TypeHierarchy {
286            major_category: "Collections".to_string(),
287            sub_category: "Sets".to_string(),
288            specific_type: "HashSet".to_string(),
289            full_type: if inner.is_empty() {
290                "HashSet".to_string()
291            } else {
292                format!("HashSet<{}>", inner)
293            },
294        }
295    } else if type_name.contains("Vec") && !type_name.contains("VecDeque") {
296        let inner = extract_generic_params(type_name, "Vec");
297        TypeHierarchy {
298            major_category: "Collections".to_string(),
299            sub_category: "Sequences".to_string(),
300            specific_type: "Vec".to_string(),
301            full_type: if inner.is_empty() {
302                "Vec".to_string()
303            } else {
304                format!("Vec<{}>", inner)
305            },
306        }
307    } else if type_name.contains("VecDeque") {
308        let inner = extract_generic_params(type_name, "VecDeque");
309        TypeHierarchy {
310            major_category: "Collections".to_string(),
311            sub_category: "Sequences".to_string(),
312            specific_type: "VecDeque".to_string(),
313            full_type: if inner.is_empty() {
314                "VecDeque".to_string()
315            } else {
316                format!("VecDeque<{}>", inner)
317            },
318        }
319    }
320    // Strings
321    else if type_name.contains("String") && !type_name.contains("<") {
322        TypeHierarchy {
323            major_category: "Strings".to_string(),
324            sub_category: "Owned".to_string(),
325            specific_type: "String".to_string(),
326            full_type: "String".to_string(),
327        }
328    } else if type_name.contains("&str") || (type_name.contains("str") && type_name.contains("&")) {
329        TypeHierarchy {
330            major_category: "Strings".to_string(),
331            sub_category: "Borrowed".to_string(),
332            specific_type: "&str".to_string(),
333            full_type: "&str".to_string(),
334        }
335    }
336    // Smart Pointers
337    else if type_name.contains("Box<") {
338        let inner = extract_generic_params(type_name, "Box");
339        TypeHierarchy {
340            major_category: "Smart Pointers".to_string(),
341            sub_category: "Owned".to_string(),
342            specific_type: "Box".to_string(),
343            full_type: if inner.is_empty() {
344                "Box".to_string()
345            } else {
346                format!("Box<{}>", inner)
347            },
348        }
349    } else if type_name.contains("Rc<") {
350        let inner = extract_generic_params(type_name, "Rc");
351        TypeHierarchy {
352            major_category: "Smart Pointers".to_string(),
353            sub_category: "Reference Counted".to_string(),
354            specific_type: "Rc".to_string(),
355            full_type: if inner.is_empty() {
356                "Rc".to_string()
357            } else {
358                format!("Rc<{}>", inner)
359            },
360        }
361    } else if type_name.contains("Arc<") {
362        let inner = extract_generic_params(type_name, "Arc");
363        TypeHierarchy {
364            major_category: "Smart Pointers".to_string(),
365            sub_category: "Thread-Safe Shared".to_string(),
366            specific_type: "Arc".to_string(),
367            full_type: if inner.is_empty() {
368                "Arc".to_string()
369            } else {
370                format!("Arc<{}>", inner)
371            },
372        }
373    }
374    // Primitives
375    else if is_primitive_type(type_name) {
376        let clean_type = type_name.split("::").last().unwrap_or(type_name);
377        let sub_cat = if clean_type.contains("i") || clean_type.contains("u") {
378            "Integers"
379        } else if clean_type.contains("f") {
380            "Floats"
381        } else if clean_type == "bool" {
382            "Boolean"
383        } else {
384            "Other"
385        };
386        TypeHierarchy {
387            major_category: "Primitives".to_string(),
388            sub_category: sub_cat.to_string(),
389            specific_type: clean_type.to_string(),
390            full_type: clean_type.to_string(),
391        }
392    }
393    // Fallback
394    else {
395        let simplified = type_name.split("::").last().unwrap_or(type_name);
396        TypeHierarchy {
397            major_category: "Custom Types".to_string(),
398            sub_category: "User Defined".to_string(),
399            specific_type: simplified.to_string(),
400            full_type: simplified.to_string(),
401        }
402    }
403}
404
405/// Extract generic parameters from type names (enhanced version)
406pub fn extract_generic_params(type_name: &str, container: &str) -> String {
407    if let Some(start) = type_name.find(&format!("{}<", container)) {
408        let start = start + container.len() + 1;
409        if let Some(end) = find_matching_bracket(type_name, start - 1) {
410            let inner = &type_name[start..end];
411            // Simplify the inner type
412            return inner.split("::").last().unwrap_or(inner).to_string();
413        }
414    }
415    String::new()
416}
417
418/// Find matching closing bracket for generic types
419fn find_matching_bracket(s: &str, start: usize) -> Option<usize> {
420    let chars: Vec<char> = s.chars().collect();
421    if start >= chars.len() || chars[start] != '<' {
422        return None;
423    }
424
425    let mut depth = 1;
426    for i in (start + 1)..chars.len() {
427        match chars[i] {
428            '<' => depth += 1,
429            '>' => {
430                depth -= 1;
431                if depth == 0 {
432                    return Some(i);
433                }
434            }
435            _ => {}
436        }
437    }
438    None
439}
440
441/// Check if a type is a primitive type
442pub fn is_primitive_type(type_name: &str) -> bool {
443    let clean_type = type_name.split("::").last().unwrap_or(type_name);
444    matches!(
445        clean_type,
446        "i8" | "i16"
447            | "i32"
448            | "i64"
449            | "i128"
450            | "isize"
451            | "u8"
452            | "u16"
453            | "u32"
454            | "u64"
455            | "u128"
456            | "usize"
457            | "f32"
458            | "f64"
459            | "bool"
460            | "char"
461    )
462}
463
464/// Extract array information for display
465pub fn extract_array_info(type_name: &str) -> String {
466    if let Some(start) = type_name.find('[') {
467        if let Some(end) = type_name.find(']') {
468            return type_name[start..=end].to_string();
469        }
470    }
471    "Array".to_string()
472}
473
474/// Extract standard library module name
475pub fn extract_std_module(type_name: &str) -> String {
476    let parts: Vec<&str> = type_name.split("::").collect();
477    if parts.len() >= 2 {
478        match parts[1] {
479            "collections" => "Collections".to_string(),
480            "sync" => "Synchronization".to_string(),
481            "thread" => "Threading".to_string(),
482            "fs" => "File System".to_string(),
483            "net" => "Networking".to_string(),
484            "io" => "Input/Output".to_string(),
485            _ => "Other".to_string(),
486        }
487    } else {
488        "Other".to_string()
489    }
490}