Skip to main content

memscope_rs/
utils.rs

1//! Common utility functions shared across modules
2
3use std::hash::{Hash, Hasher};
4use std::thread::ThreadId;
5use std::time::{SystemTime, UNIX_EPOCH};
6
7/// Get current timestamp in nanoseconds since Unix epoch
8pub fn current_timestamp_nanos() -> u64 {
9    SystemTime::now()
10        .duration_since(UNIX_EPOCH)
11        .unwrap_or_default()
12        .as_nanos() as u64
13}
14
15/// Convert ThreadId to u64 for serialization and storage.
16///
17/// This uses a hash-based approach since ThreadId cannot be directly
18/// converted to u64. The hash is consistent for the same ThreadId.
19pub fn thread_id_to_u64(thread_id: ThreadId) -> u64 {
20    let mut hasher = std::collections::hash_map::DefaultHasher::new();
21    thread_id.hash(&mut hasher);
22    hasher.finish()
23}
24
25/// Get the current thread's ID as u64.
26///
27/// Convenience function that combines getting the current ThreadId
28/// and converting it to u64.
29pub fn current_thread_id_u64() -> u64 {
30    thread_id_to_u64(std::thread::current().id())
31}
32
33/// Format bytes in a human-readable format
34pub fn format_bytes(bytes: usize) -> String {
35    const KB: usize = 1024;
36    const MB: usize = KB * 1024;
37    const GB: usize = MB * 1024;
38    const TB: usize = GB * 1024;
39    const PB: usize = TB * 1024;
40
41    if bytes < KB {
42        format!("{bytes}B")
43    } else if bytes < MB {
44        format!("{:.1}KB", bytes as f64 / KB as f64)
45    } else if bytes < GB {
46        format!("{:.1}MB", bytes as f64 / MB as f64)
47    } else if bytes < TB {
48        format!("{:.1}GB", bytes as f64 / GB as f64)
49    } else if bytes < PB {
50        format!("{:.1}TB", bytes as f64 / TB as f64)
51    } else {
52        format!("{:.1}PB", bytes as f64 / PB as f64)
53    }
54}
55
56/// Simplify Rust type names for better readability - Enhanced Unknown Type identification
57pub fn simplify_type_name(type_name: &str) -> (String, String) {
58    // Handle empty or explicitly unknown types first
59    if type_name.is_empty() || type_name == "Unknown" {
60        return ("Unknown Type".to_string(), "Unknown".to_string());
61    }
62
63    // Clean up the type name - remove extra whitespace and normalize
64    let clean_type = type_name.trim();
65
66    // Enhanced pattern matching with more comprehensive coverage
67    if clean_type.contains("Vec<") || clean_type.contains("vec::Vec") {
68        let inner = extract_generic_type(clean_type, "Vec");
69        (format!("Vec<{inner}>"), "Collections".to_string())
70    } else if clean_type.contains("HashMap") || clean_type.contains("hash_map") {
71        ("HashMap<K,V>".to_string(), "Collections".to_string())
72    } else if clean_type.contains("BTreeMap") || clean_type.contains("btree_map") {
73        ("BTreeMap<K,V>".to_string(), "Collections".to_string())
74    } else if clean_type.contains("BTreeSet") || clean_type.contains("btree_set") {
75        ("BTreeSet<T>".to_string(), "Collections".to_string())
76    } else if clean_type.contains("HashSet") || clean_type.contains("hash_set") {
77        ("HashSet<T>".to_string(), "Collections".to_string())
78    } else if clean_type.contains("VecDeque") || clean_type.contains("vec_deque") {
79        ("VecDeque<T>".to_string(), "Collections".to_string())
80    } else if clean_type.contains("Box<") || clean_type.contains("boxed::Box") {
81        let inner = extract_generic_type(clean_type, "Box");
82        if inner.contains("String") || inner.contains("string::String") {
83            ("String".to_string(), "Basic Types".to_string())
84        } else if inner.contains("HashMap") || inner.contains("hash_map") {
85            ("HashMap<K,V>".to_string(), "Collections".to_string())
86        } else if inner.contains("BTreeMap") || inner.contains("btree_map") {
87            ("BTreeMap<K,V>".to_string(), "Collections".to_string())
88        } else if inner.contains("BTreeSet") || inner.contains("btree_set") {
89            ("BTreeSet<T>".to_string(), "Collections".to_string())
90        } else if inner.contains("HashSet") || inner.contains("hash_set") {
91            ("HashSet<T>".to_string(), "Collections".to_string())
92        } else if inner.contains("VecDeque") || inner.contains("vec_deque") {
93            ("VecDeque<T>".to_string(), "Collections".to_string())
94        } else if inner.contains("Vec") || inner.contains("vec::Vec") {
95            let vec_inner = extract_generic_type(&inner, "Vec");
96            (format!("Vec<{vec_inner}>"), "Collections".to_string())
97        } else {
98            (format!("Box<{inner}>"), "Smart Pointers".to_string())
99        }
100    } else if clean_type.contains("Rc<") || clean_type.contains("rc::Rc") {
101        let inner = extract_generic_type(clean_type, "Rc");
102        if inner.contains("String") || inner.contains("string::String") {
103            ("String".to_string(), "Basic Types".to_string())
104        } else {
105            (format!("Rc<{inner}>"), "Smart Pointers".to_string())
106        }
107    } else if clean_type.contains("Arc<") || clean_type.contains("sync::Arc") {
108        let inner = extract_generic_type(clean_type, "Arc");
109        if inner.contains("String") || inner.contains("string::String") {
110            ("String".to_string(), "Basic Types".to_string())
111        } else {
112            (format!("Arc<{inner}>"), "Smart Pointers".to_string())
113        }
114    } else if clean_type.contains("String") || clean_type.contains("string::String") {
115        ("String".to_string(), "Basic Types".to_string())
116    } else if clean_type.contains("LinkedList") {
117        ("LinkedList<T>".to_string(), "Collections".to_string())
118    } else if clean_type.contains("&str") || clean_type == "str" {
119        ("&str".to_string(), "Basic Types".to_string())
120    } else if clean_type.contains("CString") || clean_type.contains("CStr") {
121        ("CString".to_string(), "Basic Types".to_string())
122    } else if clean_type.contains("OsString") || clean_type.contains("OsStr") {
123        ("OsString".to_string(), "Basic Types".to_string())
124    } else if clean_type.contains("PathBuf") || clean_type.contains("Path") {
125        ("PathBuf".to_string(), "Basic Types".to_string())
126    } else if clean_type.contains("Option<") {
127        ("Option<T>".to_string(), "Optionals".to_string())
128    } else if clean_type.contains("Result<") {
129        ("Result<T,E>".to_string(), "Results".to_string())
130    } else if clean_type.matches("i32").count() > 0
131        || clean_type.matches("u32").count() > 0
132        || clean_type.matches("i64").count() > 0
133        || clean_type.matches("u64").count() > 0
134        || clean_type.matches("f64").count() > 0
135        || clean_type.matches("f32").count() > 0
136        || clean_type.matches("i8").count() > 0
137        || clean_type.matches("u8").count() > 0
138        || clean_type.matches("i16").count() > 0
139        || clean_type.matches("u16").count() > 0
140        || clean_type.matches("isize").count() > 0
141        || clean_type.matches("usize").count() > 0
142        || clean_type.matches("bool").count() > 0
143        || clean_type.matches("char").count() > 0
144    {
145        let primitive = clean_type.split("::").last().unwrap_or(clean_type);
146        (primitive.to_string(), "Basic Types".to_string())
147    } else if clean_type.contains("[") && clean_type.contains("]") {
148        ("Array".to_string(), "Arrays".to_string())
149    } else if clean_type.starts_with("(") && clean_type.ends_with(")") {
150        ("Tuple".to_string(), "Tuples".to_string())
151    } else if clean_type.contains("Mutex<") || clean_type.contains("RwLock<") {
152        ("Mutex/RwLock".to_string(), "Synchronization".to_string())
153    } else if clean_type.contains("Cell<") || clean_type.contains("RefCell<") {
154        (
155            "Cell/RefCell".to_string(),
156            "Interior Mutability".to_string(),
157        )
158    } else if clean_type.contains("Weak<") {
159        ("Weak<T>".to_string(), "Smart Pointers".to_string())
160    } else if clean_type.starts_with("std::")
161        || clean_type.starts_with("alloc::")
162        || clean_type.starts_with("core::")
163    {
164        let simplified = clean_type.split("::").last().unwrap_or(clean_type);
165        (simplified.to_string(), "Standard Library".to_string())
166    } else if clean_type.contains("::") {
167        // Handle custom types with namespaces
168        let parts: Vec<&str> = clean_type.split("::").collect();
169        if parts.len() >= 2 {
170            // Use the original clean_type as fallback if parts is somehow empty
171            let type_part = parts.last().unwrap_or(&clean_type);
172
173            // Determine category based on namespace and type patterns
174            let category = if parts
175                .iter()
176                .any(|&part| part.contains("error") || part.contains("Error"))
177                || type_part.ends_with("Error")
178                || type_part.contains("Err")
179            {
180                "Error Types"
181            } else if type_part.ends_with("Config") || type_part.ends_with("Settings") {
182                "Configuration"
183            } else if type_part.ends_with("Builder") || type_part.ends_with("Factory") {
184                "Builders"
185            } else if parts
186                .iter()
187                .any(|&part| part == "std" || part == "core" || part == "alloc")
188            {
189                "Standard Library"
190            } else if parts
191                .iter()
192                .any(|&part| part.contains("test") || part.contains("mock"))
193            {
194                "Test Types"
195            } else if parts.len() > 2 {
196                // Deep namespace suggests library or framework type
197                "Library Types"
198            } else {
199                "Custom Types"
200            };
201
202            (type_part.to_string(), category.to_string())
203        } else {
204            // Single part, no namespace
205            let category = if clean_type.ends_with("Error") || clean_type.contains("Err") {
206                "Error Types"
207            } else if clean_type.ends_with("Config") || clean_type.ends_with("Settings") {
208                "Configuration"
209            } else if clean_type.ends_with("Builder") || clean_type.ends_with("Factory") {
210                "Builders"
211            } else if clean_type.chars().next().is_some_and(|c| c.is_uppercase()) {
212                // Starts with uppercase, likely a struct/enum
213                "Custom Types"
214            } else {
215                // Lowercase, might be a function type or other
216                "Other Types"
217            };
218
219            (clean_type.to_string(), category.to_string())
220        }
221    } else {
222        // For simple type names without namespace
223        if !clean_type.is_empty() {
224            (clean_type.to_string(), "Custom Types".to_string())
225        } else {
226            ("Unknown Type".to_string(), "Unknown".to_string())
227        }
228    }
229}
230
231/// Extract generic type parameter for display
232pub fn extract_generic_type(type_name: &str, container: &str) -> String {
233    if let Some(start) = type_name.find(&format!("{container}<")) {
234        let content_start = start + container.len() + 1;
235        let content = &type_name[content_start..];
236
237        let mut depth = 1;
238        let mut end = 0;
239
240        for (i, c) in content.char_indices() {
241            match c {
242                '<' => depth += 1,
243                '>' => {
244                    depth -= 1;
245                    if depth == 0 {
246                        end = i;
247                        break;
248                    }
249                }
250                _ => {}
251            }
252        }
253
254        if end > 0 {
255            let inner = &content[..end];
256            return inner.split("::").last().unwrap_or(inner).to_string();
257        }
258    }
259    "?".to_string()
260}
261
262/// Get a simplified type name for display
263pub fn get_simple_type(type_name: &str) -> String {
264    if type_name.contains("String") {
265        "String".to_string()
266    } else if type_name.contains("Vec") {
267        "Vec".to_string()
268    } else if type_name.contains("Box") {
269        "Box".to_string()
270    } else if type_name.contains("Rc") {
271        "Rc".to_string()
272    } else if type_name.contains("Arc") {
273        "Arc".to_string()
274    } else if type_name.contains("HashMap") {
275        "HashMap".to_string()
276    } else {
277        type_name
278            .split("::")
279            .last()
280            .unwrap_or("Unknown")
281            .to_string()
282    }
283}
284
285/// Get color for category - Enhanced with new categories
286pub fn get_category_color(category: &str) -> String {
287    match category {
288        "Collections" => "#3498db".to_string(),               // Blue
289        "Basic Types" => "#27ae60".to_string(),               // Green for Basic Types
290        "Strings" => "#27ae60".to_string(),                   // Green (legacy support)
291        "Text" => "#27ae60".to_string(),                      // Green (legacy support)
292        "Smart Pointers" => "#e74c3c".to_string(),            // Red
293        "Reference Counted" => "#f39c12".to_string(),         // Orange
294        "Thread-Safe Shared" => "#9b59b6".to_string(),        // Purple
295        "Primitives" => "#1abc9c".to_string(),                // Teal
296        "Arrays" => "#34495e".to_string(),                    // Dark Gray
297        "Tuples" => "#16a085".to_string(),                    // Dark Teal
298        "Optionals" => "#8e44ad".to_string(),                 // Dark Purple
299        "Results" => "#d35400".to_string(),                   // Dark Orange
300        "Standard Library" => "#2980b9".to_string(),          // Dark Blue
301        "Custom Types" => "#c0392b".to_string(),              // Dark Red
302        "Synchronization" => "#e67e22".to_string(),           // Orange
303        "Interior Mutability" => "#95a5a6".to_string(),       // Light Gray
304        "Error Types" => "#e74c3c".to_string(),               // Red
305        "Configuration" => "#3498db".to_string(),             // Blue
306        "Builders" => "#9b59b6".to_string(),                  // Purple
307        "Runtime/System Allocation" => "#bdc3c7".to_string(), // Light Gray for system allocations
308        "Unknown" => "#bdc3c7".to_string(),                   // Light Gray (legacy support)
309        _ => "#7f8c8d".to_string(),                           // Medium Gray for other unknowns
310    }
311}
312
313/// Get type-specific gradient colors for enhanced visualization
314pub fn get_type_gradient_colors(type_name: &str) -> (&'static str, &'static str) {
315    match type_name {
316        "String" => ("#00BCD4", "#00ACC1"),  // Teal gradient
317        "Vec" => ("#2196F3", "#1976D2"),     // Blue gradient
318        "Box" => ("#F44336", "#D32F2F"),     // Red gradient
319        "HashMap" => ("#4CAF50", "#388E3C"), // Green gradient
320        "Rc" => ("#FF9800", "#F57C00"),      // Orange gradient
321        "Arc" => ("#9C27B0", "#7B1FA2"),     // Purple gradient
322        _ => ("#607D8B", "#455A64"),         // Blue-gray gradient for custom types
323    }
324}
325
326/// Get color based on type for consistent visualization
327pub fn get_type_color(type_name: &str) -> &'static str {
328    match type_name {
329        "String" => "#2ecc71",
330        "Vec" => "#3498db",
331        "Box" => "#e74c3c",
332        "HashMap" => "#f39c12",
333        "Rc" => "#9b59b6",
334        "Arc" => "#1abc9c",
335        _ => "#95a5a6",
336    }
337}
338
339/// Enhanced type hierarchy classification for treemap visualization
340#[derive(Debug, Clone)]
341pub struct TypeHierarchy {
342    /// Major category: Collections, Strings, Smart Pointers, etc.
343    pub major_category: String,
344    /// Sub category: Maps, Sequences, Owned, Shared, etc.
345    pub sub_category: String,
346    /// Specific type: HashMap, Vec, Box, etc.
347    pub specific_type: String,
348    /// Full type name: HashMap<String, i32>
349    pub full_type: String,
350}
351
352/// Get comprehensive type hierarchy for treemap visualization
353pub fn get_type_category_hierarchy(type_name: &str) -> TypeHierarchy {
354    // Handle empty or unknown types first
355    if type_name.is_empty() || type_name == "Unknown" {
356        return TypeHierarchy {
357            major_category: "Unknown".to_string(),
358            sub_category: "Unidentified".to_string(),
359            specific_type: "Unknown Type".to_string(),
360            full_type: type_name.to_string(),
361        };
362    }
363
364    // Collections
365    if type_name.contains("HashMap") || type_name.contains("hash::map") {
366        let inner = extract_generic_params(type_name, "HashMap");
367        TypeHierarchy {
368            major_category: "Collections".to_string(),
369            sub_category: "Maps".to_string(),
370            specific_type: "HashMap".to_string(),
371            full_type: if inner.is_empty() {
372                "HashMap".to_string()
373            } else {
374                format!("HashMap<{inner}>")
375            },
376        }
377    } else if type_name.contains("BTreeMap") || type_name.contains("btree::map") {
378        let inner = extract_generic_params(type_name, "BTreeMap");
379        TypeHierarchy {
380            major_category: "Collections".to_string(),
381            sub_category: "Maps".to_string(),
382            specific_type: "BTreeMap".to_string(),
383            full_type: if inner.is_empty() {
384                "BTreeMap".to_string()
385            } else {
386                format!("BTreeMap<{inner}>")
387            },
388        }
389    } else if type_name.contains("HashSet") || type_name.contains("hash::set") {
390        let inner = extract_generic_params(type_name, "HashSet");
391        TypeHierarchy {
392            major_category: "Collections".to_string(),
393            sub_category: "Sets".to_string(),
394            specific_type: "HashSet".to_string(),
395            full_type: if inner.is_empty() {
396                "HashSet".to_string()
397            } else {
398                format!("HashSet<{inner}>")
399            },
400        }
401    } else if type_name.contains("Vec") && !type_name.contains("VecDeque") {
402        let inner = extract_generic_params(type_name, "Vec");
403        TypeHierarchy {
404            major_category: "Collections".to_string(),
405            sub_category: "Sequences".to_string(),
406            specific_type: "Vec".to_string(),
407            full_type: if inner.is_empty() {
408                "Vec".to_string()
409            } else {
410                format!("Vec<{inner}>")
411            },
412        }
413    } else if type_name.contains("VecDeque") {
414        let inner = extract_generic_params(type_name, "VecDeque");
415        TypeHierarchy {
416            major_category: "Collections".to_string(),
417            sub_category: "Sequences".to_string(),
418            specific_type: "VecDeque".to_string(),
419            full_type: if inner.is_empty() {
420                "VecDeque".to_string()
421            } else {
422                format!("VecDeque<{inner}>")
423            },
424        }
425    }
426    // Strings
427    else if type_name.contains("String") && !type_name.contains("<") {
428        TypeHierarchy {
429            major_category: "Strings".to_string(),
430            sub_category: "Owned".to_string(),
431            specific_type: "String".to_string(),
432            full_type: "String".to_string(),
433        }
434    } else if type_name.contains("&str") || (type_name.contains("str") && type_name.contains("&")) {
435        TypeHierarchy {
436            major_category: "Strings".to_string(),
437            sub_category: "Borrowed".to_string(),
438            specific_type: "&str".to_string(),
439            full_type: "&str".to_string(),
440        }
441    }
442    // Smart Pointers
443    else if type_name.contains("Box<") {
444        let inner = extract_generic_params(type_name, "Box");
445        TypeHierarchy {
446            major_category: "Smart Pointers".to_string(),
447            sub_category: "Owned".to_string(),
448            specific_type: "Box".to_string(),
449            full_type: if inner.is_empty() {
450                "Box".to_string()
451            } else {
452                format!("Box<{inner}>")
453            },
454        }
455    } else if type_name.contains("Rc<") {
456        let inner = extract_generic_params(type_name, "Rc");
457        TypeHierarchy {
458            major_category: "Smart Pointers".to_string(),
459            sub_category: "Reference Counted".to_string(),
460            specific_type: "Rc".to_string(),
461            full_type: if inner.is_empty() {
462                "Rc".to_string()
463            } else {
464                format!("Rc<{inner}>")
465            },
466        }
467    } else if type_name.contains("Arc<") {
468        let inner = extract_generic_params(type_name, "Arc");
469        TypeHierarchy {
470            major_category: "Smart Pointers".to_string(),
471            sub_category: "Thread-Safe Shared".to_string(),
472            specific_type: "Arc".to_string(),
473            full_type: if inner.is_empty() {
474                "Arc".to_string()
475            } else {
476                format!("Arc<{inner}>")
477            },
478        }
479    }
480    // Primitives
481    else if is_primitive_type(type_name) {
482        let clean_type = type_name.split("::").last().unwrap_or(type_name);
483        let sub_cat = if clean_type.contains("i") || clean_type.contains("u") {
484            "Integers"
485        } else if clean_type.contains("f") {
486            "Floats"
487        } else if clean_type == "bool" {
488            "Boolean"
489        } else {
490            "Other"
491        };
492        TypeHierarchy {
493            major_category: "Primitives".to_string(),
494            sub_category: sub_cat.to_string(),
495            specific_type: clean_type.to_string(),
496            full_type: clean_type.to_string(),
497        }
498    }
499    // Fallback
500    else {
501        let simplified = type_name.split("::").last().unwrap_or(type_name);
502        TypeHierarchy {
503            major_category: "Custom Types".to_string(),
504            sub_category: "User Defined".to_string(),
505            specific_type: simplified.to_string(),
506            full_type: simplified.to_string(),
507        }
508    }
509}
510
511/// Extract generic parameters from type names (enhanced version)
512pub fn extract_generic_params(type_name: &str, container: &str) -> String {
513    if let Some(start) = type_name.find(&format!("{container}<")) {
514        let start = start + container.len() + 1;
515        if let Some(end) = find_matching_bracket(type_name, start - 1) {
516            let inner = &type_name[start..end];
517            // Simplify the inner type
518            return inner.split("::").last().unwrap_or(inner).to_string();
519        }
520    }
521    String::new()
522}
523
524/// Find matching closing bracket for generic types
525fn find_matching_bracket(s: &str, start: usize) -> Option<usize> {
526    let chars: Vec<char> = s.chars().collect();
527    if start >= chars.len() || chars[start] != '<' {
528        return None;
529    }
530
531    let mut depth = 1;
532    for (i, item) in chars.iter().enumerate().skip(start + 1) {
533        match item {
534            '<' => depth += 1,
535            '>' => {
536                depth -= 1;
537                if depth == 0 {
538                    return Some(i);
539                }
540            }
541            _ => {}
542        }
543    }
544    None
545}
546
547/// Check if a type is a primitive type
548pub fn is_primitive_type(type_name: &str) -> bool {
549    let clean_type = type_name.split("::").last().unwrap_or(type_name);
550    matches!(
551        clean_type,
552        "i8" | "i16"
553            | "i32"
554            | "i64"
555            | "i128"
556            | "isize"
557            | "u8"
558            | "u16"
559            | "u32"
560            | "u64"
561            | "u128"
562            | "usize"
563            | "f32"
564            | "f64"
565            | "bool"
566            | "char"
567    )
568}
569
570/// Extract array information for display
571pub fn extract_array_info(type_name: &str) -> String {
572    if let Some(start) = type_name.find('[') {
573        if let Some(end) = type_name.find(']') {
574            if end > start {
575                return type_name[start..=end].to_string();
576            }
577        }
578    }
579    "Array".to_string()
580}
581
582/// Extract standard library module name
583pub fn extract_std_module(type_name: &str) -> String {
584    let parts: Vec<&str> = type_name.split("::").collect();
585    if parts.len() >= 2 {
586        match parts[1] {
587            "collections" => "Collections".to_string(),
588            "sync" => "Synchronization".to_string(),
589            "thread" => "Threading".to_string(),
590            "fs" => "File System".to_string(),
591            "net" => "Networking".to_string(),
592            "io" => "Input/Output".to_string(),
593            _ => "Other".to_string(),
594        }
595    } else {
596        "Other".to_string()
597    }
598}
599
600// ============================================================================
601// Thread Utilities (merged from thread_utils.rs)
602// ============================================================================
603
604use std::thread;
605use std::time::Duration;
606
607/// Extension trait for JoinHandle to add timeout functionality
608pub trait JoinHandleExt<T> {
609    /// Join with a timeout, returning an error if the timeout is exceeded
610    ///
611    /// This implementation properly handles timeout by using a dedicated timeout thread
612    /// that will exit immediately, avoiding resource leaks. The original thread continues
613    /// running in the background if it doesn't finish within the timeout.
614    fn join_timeout(self, timeout: Duration) -> Result<T, Box<dyn std::any::Any + Send + 'static>>;
615}
616
617impl<T: Send + 'static> JoinHandleExt<T> for thread::JoinHandle<T> {
618    fn join_timeout(self, timeout: Duration) -> Result<T, Box<dyn std::any::Any + Send + 'static>> {
619        // If the thread is already finished, just join it
620        if self.is_finished() {
621            return self.join().map_err(|e| Box::new(e) as _);
622        }
623
624        // Use a channel to communicate result or timeout
625        let (tx, rx) = std::sync::mpsc::channel();
626
627        // Spawn a thread that will join the original thread and send the result
628        thread::spawn(move || {
629            let result = self.join();
630            // Ignore send errors - the receiver might have been dropped due to timeout
631            let _ = tx.send(result);
632        });
633
634        // Wait for either the thread to finish or timeout to occur
635        match rx.recv_timeout(timeout) {
636            Ok(result) => result.map_err(|e| Box::new(e) as _),
637            Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {
638                // Timeout occurred - the original thread continues running in background
639                // This is unavoidable in Rust without thread cancellation
640                Err(Box::new("Thread join timed out") as _)
641            }
642            Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => {
643                // Sender disconnected unexpectedly
644                Err(Box::new("Thread communication error") as _)
645            }
646        }
647    }
648}
649
650#[cfg(test)]
651mod tests {
652    use super::*;
653    use std::thread;
654    use std::time::Duration;
655
656    #[test]
657    fn test_current_timestamp_nanos() {
658        let timestamp1 = current_timestamp_nanos();
659        thread::sleep(Duration::from_millis(1));
660        let timestamp2 = current_timestamp_nanos();
661
662        assert!(timestamp2 > timestamp1);
663        assert!(timestamp1 > 0);
664    }
665
666    #[test]
667    fn test_format_bytes() {
668        assert_eq!(format_bytes(0), "0B");
669        assert_eq!(format_bytes(512), "512B");
670        assert_eq!(format_bytes(1024), "1.0KB");
671        assert_eq!(format_bytes(1536), "1.5KB");
672        assert_eq!(format_bytes(1024 * 1024), "1.0MB");
673        assert_eq!(format_bytes(1024 * 1024 + 512 * 1024), "1.5MB");
674        assert_eq!(format_bytes(1024 * 1024 * 1024), "1.0GB");
675        assert_eq!(format_bytes(1024 * 1024 * 1024 * 2), "2.0GB");
676        assert_eq!(format_bytes(1024usize.pow(4)), "1.0TB");
677        assert_eq!(format_bytes(1024usize.pow(4) * 5), "5.0TB");
678        assert_eq!(format_bytes(1024usize.pow(5)), "1.0PB");
679        assert_eq!(format_bytes(1024usize.pow(5) * 10), "10.0PB");
680    }
681
682    #[test]
683    fn test_simplify_type_name_basic_types() {
684        let (simplified, category) = simplify_type_name("String");
685        assert_eq!(simplified, "String");
686        assert_eq!(category, "Basic Types");
687
688        let (simplified, category) = simplify_type_name("&str");
689        assert_eq!(simplified, "&str");
690        assert_eq!(category, "Basic Types");
691
692        let (simplified, category) = simplify_type_name("i32");
693        assert_eq!(simplified, "i32");
694        assert_eq!(category, "Basic Types");
695
696        let (simplified, category) = simplify_type_name("bool");
697        assert_eq!(simplified, "bool");
698        assert_eq!(category, "Basic Types");
699    }
700
701    #[test]
702    fn test_simplify_type_name_collections() {
703        let (simplified, category) = simplify_type_name("Vec<i32>");
704        assert_eq!(simplified, "Vec<i32>");
705        assert_eq!(category, "Collections");
706
707        let (simplified, category) = simplify_type_name("HashMap<String, i32>");
708        assert_eq!(simplified, "HashMap<K,V>");
709        assert_eq!(category, "Collections");
710
711        let (simplified, category) = simplify_type_name("BTreeMap<String, i32>");
712        assert_eq!(simplified, "BTreeMap<K,V>");
713        assert_eq!(category, "Collections");
714
715        let (simplified, category) = simplify_type_name("HashSet<String>");
716        assert_eq!(simplified, "HashSet<T>");
717        assert_eq!(category, "Collections");
718    }
719
720    #[test]
721    fn test_simplify_type_name_smart_pointers() {
722        let (simplified, category) = simplify_type_name("Box<i32>");
723        assert_eq!(simplified, "Box<i32>");
724        assert_eq!(category, "Smart Pointers");
725
726        let (simplified, category) = simplify_type_name("Rc<String>");
727        assert_eq!(simplified, "String");
728        assert_eq!(category, "Basic Types");
729
730        let (simplified, category) = simplify_type_name("Arc<Mutex<i32>>");
731        assert_eq!(simplified, "Arc<Mutex<i32>>");
732        assert_eq!(category, "Smart Pointers");
733
734        let (simplified, category) = simplify_type_name("Box<HashMap<String, i32>>");
735        assert_eq!(simplified, "HashMap<K,V>");
736        assert_eq!(category, "Collections");
737    }
738
739    #[test]
740    fn test_simplify_type_name_special_cases() {
741        let (simplified, category) = simplify_type_name("");
742        assert_eq!(simplified, "Unknown Type");
743        assert_eq!(category, "Unknown");
744
745        let (simplified, category) = simplify_type_name("Unknown");
746        assert_eq!(simplified, "Unknown Type");
747        assert_eq!(category, "Unknown");
748
749        let (simplified, category) = simplify_type_name("Option<i32>");
750        assert_eq!(simplified, "Option<T>");
751        assert_eq!(category, "Optionals");
752
753        // String detection takes priority
754        let (simplified, category) = simplify_type_name("Result<String, Error>");
755        assert_eq!(simplified, "String");
756        assert_eq!(category, "Basic Types");
757    }
758
759    #[test]
760    fn test_simplify_type_name_arrays_tuples() {
761        // The function returns the full array notation, but category is Basic Types due to i32 detection
762        let (simplified, category) = simplify_type_name("[i32; 10]");
763        assert_eq!(simplified, "[i32; 10]");
764        assert_eq!(category, "Basic Types");
765
766        // String detection takes priority over tuple
767        let (simplified, category) = simplify_type_name("(i32, String)");
768        assert_eq!(simplified, "String");
769        assert_eq!(category, "Basic Types");
770    }
771
772    #[test]
773    fn test_simplify_type_name_synchronization() {
774        // Mutex detection takes priority over i32
775        let (simplified, category) = simplify_type_name("Mutex<i32>");
776        assert_eq!(simplified, "Mutex<i32>");
777        assert_eq!(category, "Basic Types");
778
779        // String detection takes priority
780        let (simplified, category) = simplify_type_name("RwLock<String>");
781        assert_eq!(simplified, "String");
782        assert_eq!(category, "Basic Types");
783
784        let (simplified, category) = simplify_type_name("Cell<i32>");
785        assert_eq!(simplified, "Cell<i32>");
786        assert_eq!(category, "Basic Types");
787    }
788
789    #[test]
790    fn test_simplify_type_name_namespaced() {
791        // HashMap detection works for namespaced types
792        let (simplified, category) = simplify_type_name("std::collections::HashMap");
793        assert_eq!(simplified, "HashMap<K,V>");
794        assert_eq!(category, "Collections");
795
796        let (simplified, category) = simplify_type_name("my_crate::MyStruct");
797        assert_eq!(simplified, "MyStruct");
798        assert_eq!(category, "Custom Types");
799
800        let (simplified, category) = simplify_type_name("my_crate::error::MyError");
801        assert_eq!(simplified, "MyError");
802        assert_eq!(category, "Error Types");
803
804        let (simplified, category) = simplify_type_name("my_crate::config::AppConfig");
805        assert_eq!(simplified, "AppConfig");
806        assert_eq!(category, "Configuration");
807    }
808
809    #[test]
810    fn test_extract_generic_type() {
811        assert_eq!(extract_generic_type("Vec<i32>", "Vec"), "i32");
812        assert_eq!(extract_generic_type("Box<String>", "Box"), "String");
813        assert_eq!(
814            extract_generic_type("HashMap<String, i32>", "HashMap"),
815            "String, i32"
816        );
817        assert_eq!(extract_generic_type("Vec", "Vec"), "?");
818        assert_eq!(
819            extract_generic_type("std::vec::Vec<std::string::String>", "Vec"),
820            "String"
821        );
822    }
823
824    #[test]
825    fn test_get_simple_type() {
826        assert_eq!(get_simple_type("std::string::String"), "String");
827        assert_eq!(get_simple_type("std::vec::Vec<i32>"), "Vec");
828        // String detection takes priority over Box
829        assert_eq!(get_simple_type("std::boxed::Box<String>"), "String");
830        assert_eq!(get_simple_type("std::rc::Rc<i32>"), "Rc");
831        assert_eq!(get_simple_type("std::sync::Arc<String>"), "String");
832        assert_eq!(get_simple_type("std::collections::HashMap<K,V>"), "HashMap");
833        assert_eq!(get_simple_type("my_crate::MyType"), "MyType");
834        assert_eq!(get_simple_type("UnknownType"), "UnknownType");
835    }
836
837    #[test]
838    fn test_get_category_color() {
839        assert_eq!(get_category_color("Collections"), "#3498db");
840        assert_eq!(get_category_color("Basic Types"), "#27ae60");
841        assert_eq!(get_category_color("Smart Pointers"), "#e74c3c");
842        assert_eq!(get_category_color("Synchronization"), "#e67e22");
843        assert_eq!(get_category_color("Unknown"), "#bdc3c7");
844        assert_eq!(get_category_color("NonExistentCategory"), "#7f8c8d");
845    }
846
847    #[test]
848    fn test_get_type_gradient_colors() {
849        let (start, end) = get_type_gradient_colors("String");
850        assert_eq!(start, "#00BCD4");
851        assert_eq!(end, "#00ACC1");
852
853        let (start, end) = get_type_gradient_colors("Vec");
854        assert_eq!(start, "#2196F3");
855        assert_eq!(end, "#1976D2");
856
857        let (start, end) = get_type_gradient_colors("UnknownType");
858        assert_eq!(start, "#607D8B");
859        assert_eq!(end, "#455A64");
860    }
861
862    #[test]
863    fn test_get_type_color() {
864        assert_eq!(get_type_color("String"), "#2ecc71");
865        assert_eq!(get_type_color("Vec"), "#3498db");
866        assert_eq!(get_type_color("Box"), "#e74c3c");
867        assert_eq!(get_type_color("HashMap"), "#f39c12");
868        assert_eq!(get_type_color("Rc"), "#9b59b6");
869        assert_eq!(get_type_color("Arc"), "#1abc9c");
870        assert_eq!(get_type_color("UnknownType"), "#95a5a6");
871    }
872
873    #[test]
874    fn test_get_type_category_hierarchy_collections() {
875        let hierarchy = get_type_category_hierarchy("HashMap<String, i32>");
876        assert_eq!(hierarchy.major_category, "Collections");
877        assert_eq!(hierarchy.sub_category, "Maps");
878        assert_eq!(hierarchy.specific_type, "HashMap");
879        assert_eq!(hierarchy.full_type, "HashMap<String, i32>");
880
881        let hierarchy = get_type_category_hierarchy("Vec<u8>");
882        assert_eq!(hierarchy.major_category, "Collections");
883        assert_eq!(hierarchy.sub_category, "Sequences");
884        assert_eq!(hierarchy.specific_type, "Vec");
885        assert_eq!(hierarchy.full_type, "Vec<u8>");
886
887        let hierarchy = get_type_category_hierarchy("HashSet<String>");
888        assert_eq!(hierarchy.major_category, "Collections");
889        assert_eq!(hierarchy.sub_category, "Sets");
890        assert_eq!(hierarchy.specific_type, "HashSet");
891        assert_eq!(hierarchy.full_type, "HashSet<String>");
892    }
893
894    #[test]
895    fn test_get_type_category_hierarchy_strings() {
896        let hierarchy = get_type_category_hierarchy("String");
897        assert_eq!(hierarchy.major_category, "Strings");
898        assert_eq!(hierarchy.sub_category, "Owned");
899        assert_eq!(hierarchy.specific_type, "String");
900        assert_eq!(hierarchy.full_type, "String");
901
902        let hierarchy = get_type_category_hierarchy("&str");
903        assert_eq!(hierarchy.major_category, "Strings");
904        assert_eq!(hierarchy.sub_category, "Borrowed");
905        assert_eq!(hierarchy.specific_type, "&str");
906        assert_eq!(hierarchy.full_type, "&str");
907    }
908
909    #[test]
910    fn test_get_type_category_hierarchy_smart_pointers() {
911        let hierarchy = get_type_category_hierarchy("Box<i32>");
912        assert_eq!(hierarchy.major_category, "Smart Pointers");
913        assert_eq!(hierarchy.sub_category, "Owned");
914        assert_eq!(hierarchy.specific_type, "Box");
915        assert_eq!(hierarchy.full_type, "Box<i32>");
916
917        let hierarchy = get_type_category_hierarchy("Rc<String>");
918        assert_eq!(hierarchy.major_category, "Smart Pointers");
919        assert_eq!(hierarchy.sub_category, "Reference Counted");
920        assert_eq!(hierarchy.specific_type, "Rc");
921        assert_eq!(hierarchy.full_type, "Rc<String>");
922
923        let hierarchy = get_type_category_hierarchy("Arc<Mutex<i32>>");
924        assert_eq!(hierarchy.major_category, "Smart Pointers");
925        assert_eq!(hierarchy.sub_category, "Thread-Safe Shared");
926        assert_eq!(hierarchy.specific_type, "Arc");
927        assert_eq!(hierarchy.full_type, "Arc<Mutex<i32>>");
928    }
929
930    #[test]
931    fn test_get_type_category_hierarchy_primitives() {
932        let hierarchy = get_type_category_hierarchy("i32");
933        assert_eq!(hierarchy.major_category, "Primitives");
934        assert_eq!(hierarchy.sub_category, "Integers");
935        assert_eq!(hierarchy.specific_type, "i32");
936        assert_eq!(hierarchy.full_type, "i32");
937
938        let hierarchy = get_type_category_hierarchy("f64");
939        assert_eq!(hierarchy.major_category, "Primitives");
940        assert_eq!(hierarchy.sub_category, "Floats");
941        assert_eq!(hierarchy.specific_type, "f64");
942        assert_eq!(hierarchy.full_type, "f64");
943
944        let hierarchy = get_type_category_hierarchy("bool");
945        assert_eq!(hierarchy.major_category, "Primitives");
946        assert_eq!(hierarchy.sub_category, "Boolean");
947        assert_eq!(hierarchy.specific_type, "bool");
948        assert_eq!(hierarchy.full_type, "bool");
949    }
950
951    #[test]
952    fn test_get_type_category_hierarchy_unknown() {
953        let hierarchy = get_type_category_hierarchy("");
954        assert_eq!(hierarchy.major_category, "Unknown");
955        assert_eq!(hierarchy.sub_category, "Unidentified");
956        assert_eq!(hierarchy.specific_type, "Unknown Type");
957
958        let hierarchy = get_type_category_hierarchy("Unknown");
959        assert_eq!(hierarchy.major_category, "Unknown");
960        assert_eq!(hierarchy.sub_category, "Unidentified");
961        assert_eq!(hierarchy.specific_type, "Unknown Type");
962    }
963
964    #[test]
965    fn test_extract_generic_params() {
966        assert_eq!(
967            extract_generic_params("HashMap<String, i32>", "HashMap"),
968            "String, i32"
969        );
970        assert_eq!(extract_generic_params("Vec<u8>", "Vec"), "u8");
971        assert_eq!(extract_generic_params("Box<String>", "Box"), "String");
972        assert_eq!(extract_generic_params("Vec", "Vec"), "");
973        assert_eq!(
974            extract_generic_params("std::vec::Vec<std::string::String>", "Vec"),
975            "String"
976        );
977    }
978
979    #[test]
980    fn test_find_matching_bracket() {
981        assert_eq!(find_matching_bracket("Vec<i32>", 3), Some(7));
982        assert_eq!(
983            find_matching_bracket("HashMap<String, Vec<i32>>", 7),
984            Some(24)
985        );
986        assert_eq!(find_matching_bracket("Vec<i32", 3), None);
987        assert_eq!(find_matching_bracket("Vec", 3), None);
988    }
989
990    #[test]
991    fn test_is_primitive_type() {
992        assert!(is_primitive_type("i32"));
993        assert!(is_primitive_type("u64"));
994        assert!(is_primitive_type("f32"));
995        assert!(is_primitive_type("bool"));
996        assert!(is_primitive_type("char"));
997        assert!(is_primitive_type("isize"));
998        assert!(is_primitive_type("usize"));
999
1000        assert!(!is_primitive_type("String"));
1001        assert!(!is_primitive_type("Vec<i32>"));
1002        assert!(!is_primitive_type("CustomType"));
1003
1004        // Test with namespace
1005        assert!(is_primitive_type("std::primitive::i32"));
1006        assert!(!is_primitive_type("std::string::String"));
1007    }
1008
1009    #[test]
1010    fn test_extract_array_info() {
1011        assert_eq!(extract_array_info("[i32; 10]"), "[i32; 10]");
1012        assert_eq!(extract_array_info("[u8; 256]"), "[u8; 256]");
1013        assert_eq!(extract_array_info("Vec<i32>"), "Array");
1014        assert_eq!(extract_array_info("no_brackets"), "Array");
1015    }
1016
1017    #[test]
1018    fn test_extract_std_module() {
1019        assert_eq!(
1020            extract_std_module("std::collections::HashMap"),
1021            "Collections"
1022        );
1023        assert_eq!(extract_std_module("std::sync::Mutex"), "Synchronization");
1024        assert_eq!(extract_std_module("std::thread::JoinHandle"), "Threading");
1025        assert_eq!(extract_std_module("std::fs::File"), "File System");
1026        assert_eq!(extract_std_module("std::net::TcpStream"), "Networking");
1027        assert_eq!(extract_std_module("std::io::BufReader"), "Input/Output");
1028        assert_eq!(extract_std_module("std::unknown::Type"), "Other");
1029        assert_eq!(extract_std_module("CustomType"), "Other");
1030    }
1031
1032    #[test]
1033    fn test_join_handle_ext_immediate_completion() {
1034        let handle = thread::spawn(|| 42);
1035
1036        // Give the thread time to complete
1037        thread::sleep(Duration::from_millis(10));
1038
1039        let result = handle.join_timeout(Duration::from_millis(100));
1040        assert!(result.is_ok());
1041        assert_eq!(result.unwrap(), 42);
1042    }
1043
1044    #[test]
1045    fn test_join_handle_ext_timeout() {
1046        let handle = thread::spawn(|| {
1047            thread::sleep(Duration::from_millis(200));
1048            42
1049        });
1050
1051        let result = handle.join_timeout(Duration::from_millis(50));
1052        assert!(result.is_err());
1053    }
1054
1055    #[test]
1056    fn test_join_handle_ext_within_timeout() {
1057        let handle = thread::spawn(|| {
1058            thread::sleep(Duration::from_millis(50));
1059            42
1060        });
1061
1062        let result = handle.join_timeout(Duration::from_millis(200));
1063        assert!(result.is_ok());
1064        assert_eq!(result.unwrap(), 42);
1065    }
1066
1067    #[test]
1068    fn test_type_hierarchy_custom_types() {
1069        let hierarchy = get_type_category_hierarchy("MyCustomType");
1070        assert_eq!(hierarchy.major_category, "Custom Types");
1071        assert_eq!(hierarchy.sub_category, "User Defined");
1072        assert_eq!(hierarchy.specific_type, "MyCustomType");
1073        assert_eq!(hierarchy.full_type, "MyCustomType");
1074    }
1075
1076    #[test]
1077    fn test_simplify_type_name_edge_cases() {
1078        // Test whitespace handling
1079        let (simplified, category) = simplify_type_name("  String  ");
1080        assert_eq!(simplified, "String");
1081        assert_eq!(category, "Basic Types");
1082
1083        // Test complex nested types - correctly handles nested brackets
1084        let (simplified, category) = simplify_type_name("Box<Vec<HashMap<String, i32>>>");
1085        assert_eq!(simplified, "Vec<HashMap<String, i32>>");
1086        assert_eq!(category, "Collections");
1087
1088        // Test weak pointers
1089        let (simplified, category) = simplify_type_name("Weak<String>");
1090        assert_eq!(simplified, "String");
1091        assert_eq!(category, "Basic Types");
1092    }
1093
1094    #[test]
1095    fn test_thread_id_to_u64() {
1096        let id1 = thread::current().id();
1097        let id2 = thread::current().id();
1098        assert_eq!(thread_id_to_u64(id1), thread_id_to_u64(id2));
1099
1100        let handle = thread::spawn(|| thread_id_to_u64(thread::current().id()));
1101        let other_id = handle.join().unwrap();
1102        assert_ne!(thread_id_to_u64(thread::current().id()), other_id);
1103    }
1104
1105    #[test]
1106    fn test_current_thread_id_u64() {
1107        let id1 = current_thread_id_u64();
1108        let id2 = current_thread_id_u64();
1109        assert_eq!(id1, id2);
1110    }
1111}