1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct ClosureInfo {
6 pub ptr: usize,
7 pub captures: Vec<CaptureInfo>,
8 pub creation_timestamp: u64,
9 pub thread_id: String,
10 pub call_site: String,
11 pub memory_footprint: ClosureFootprint,
12 pub optimization_potential: OptimizationPotential,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct CaptureInfo {
17 pub var_name: String,
18 pub var_ptr: usize,
19 pub mode: CaptureMode,
20 pub var_type: String,
21 pub size: usize,
22 pub lifetime_bound: Option<String>,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
26pub enum CaptureMode {
27 ByValue,
28 ByReference,
29 ByMutableReference,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct ClosureFootprint {
34 pub total_size: usize,
35 pub capture_count: usize,
36 pub by_value_count: usize,
37 pub by_ref_count: usize,
38 pub by_mut_ref_count: usize,
39 pub estimated_heap_usage: usize,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct OptimizationPotential {
44 pub level: OptimizationLevel,
45 pub potential_savings: usize,
46 pub suggestions: Vec<String>,
47}
48
49#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
50pub enum OptimizationLevel {
51 None,
52 Low,
53 Medium,
54 High,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct CaptureEvent {
59 pub closure_ptr: usize,
60 pub captured_var: CaptureInfo,
61 pub event_type: CaptureEventType,
62 pub timestamp: u64,
63}
64
65#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
66pub enum CaptureEventType {
67 Captured,
68 Released,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct DetectedClosure {
73 pub ptr: usize,
74 pub type_name: String,
75 pub size: usize,
76 pub estimated_captures: usize,
77 pub closure_type: ClosureType,
78 pub creation_context: CreationContext,
79 pub memory_impact: MemoryImpact,
80}
81
82#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
83pub enum ClosureType {
84 Fn,
85 FnMut,
86 FnOnce,
87 Unknown,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct CreationContext {
92 pub scope_name: Option<String>,
93 pub thread_id: String,
94 pub timestamp: u64,
95}
96
97#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
98pub enum MemoryImpact {
99 Minimal,
100 Low,
101 Medium,
102 High,
103 VeryHigh,
104}
105
106#[derive(Debug, Clone, Default, Serialize, Deserialize)]
107pub struct CaptureStatistics {
108 pub total_closures: usize,
109 pub total_captures: usize,
110 pub avg_captures_per_closure: f64,
111 pub total_memory_usage: usize,
112 pub captures_by_mode: HashMap<CaptureMode, usize>,
113 pub captures_by_type: HashMap<String, usize>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct OptimizationSuggestion {
118 pub category: OptimizationCategory,
119 pub priority: SuggestionPriority,
120 pub description: String,
121 pub recommendation: String,
122 pub estimated_impact: String,
123}
124
125#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
126pub enum OptimizationCategory {
127 Memory,
128 Performance,
129 Lifetime,
130}
131
132#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
133pub enum SuggestionPriority {
134 Low,
135 Medium,
136 High,
137 Critical,
138}
139
140#[derive(Debug)]
141pub struct LifetimeGraph {
142 relationships: HashMap<usize, Vec<LifetimeRelationship>>,
143}
144
145impl Default for LifetimeGraph {
146 fn default() -> Self {
147 Self::new()
148 }
149}
150
151impl LifetimeGraph {
152 pub fn new() -> Self {
153 Self {
154 relationships: HashMap::new(),
155 }
156 }
157
158 pub fn add_closure_relationships(&mut self, closure_ptr: usize, captures: &[CaptureInfo]) {
159 let relationships: Vec<LifetimeRelationship> = captures
160 .iter()
161 .map(|capture| LifetimeRelationship {
162 captured_var_ptr: capture.var_ptr,
163 capture_mode: capture.mode.clone(),
164 relationship_type: self.classify_relationship(&capture.mode),
165 })
166 .collect();
167
168 self.relationships.insert(closure_ptr, relationships);
169 }
170
171 pub fn remove_closure(&mut self, closure_ptr: usize) {
172 self.relationships.remove(&closure_ptr);
173 }
174
175 fn classify_relationship(&self, mode: &CaptureMode) -> RelationshipType {
176 match mode {
177 CaptureMode::ByValue => RelationshipType::Ownership,
178 CaptureMode::ByReference => RelationshipType::SharedBorrow,
179 CaptureMode::ByMutableReference => RelationshipType::ExclusiveBorrow,
180 }
181 }
182
183 pub fn analyze_lifetimes(&self) -> LifetimeAnalysis {
184 let mut potential_issues = Vec::new();
185 let mut lifetime_patterns = Vec::new();
186
187 for (closure_ptr, relationships) in &self.relationships {
188 let has_value_captures = relationships
189 .iter()
190 .any(|r| r.capture_mode == CaptureMode::ByValue);
191 let has_ref_captures = relationships.iter().any(|r| {
192 matches!(
193 r.capture_mode,
194 CaptureMode::ByReference | CaptureMode::ByMutableReference
195 )
196 });
197
198 if has_value_captures && has_ref_captures {
199 potential_issues.push(LifetimeIssue {
200 closure_ptr: *closure_ptr,
201 issue_type: LifetimeIssueType::MixedCaptureMode,
202 description: "Closure mixes value and reference captures".to_string(),
203 severity: IssueSeverity::Medium,
204 suggestion: "Consider consistent capture strategy".to_string(),
205 });
206 }
207
208 if relationships.len() > 5 {
209 lifetime_patterns.push(LifetimePattern {
210 pattern_type: LifetimePatternType::ManyCaptures,
211 description: format!("Closure captures {} variables", relationships.len()),
212 impact: if relationships.len() > 10 {
213 PatternImpact::High
214 } else {
215 PatternImpact::Medium
216 },
217 });
218 }
219 }
220
221 LifetimeAnalysis {
222 total_relationships: self.relationships.len(),
223 potential_issues,
224 lifetime_patterns,
225 }
226 }
227}
228
229#[derive(Debug, Clone)]
230pub struct LifetimeRelationship {
231 pub captured_var_ptr: usize,
232 pub capture_mode: CaptureMode,
233 pub relationship_type: RelationshipType,
234}
235
236#[derive(Debug, Clone, PartialEq)]
237pub enum RelationshipType {
238 Ownership,
239 SharedBorrow,
240 ExclusiveBorrow,
241}
242
243#[derive(Debug, Clone, Default, Serialize, Deserialize)]
244pub struct LifetimeAnalysis {
245 pub total_relationships: usize,
246 pub potential_issues: Vec<LifetimeIssue>,
247 pub lifetime_patterns: Vec<LifetimePattern>,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct LifetimeIssue {
252 pub closure_ptr: usize,
253 pub issue_type: LifetimeIssueType,
254 pub description: String,
255 pub severity: IssueSeverity,
256 pub suggestion: String,
257}
258
259#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
260pub enum LifetimeIssueType {
261 MixedCaptureMode,
262 PotentialDanglingReference,
263 UnnecessaryCapture,
264}
265
266#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
267pub enum IssueSeverity {
268 Low,
269 Medium,
270 High,
271 Critical,
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct LifetimePattern {
276 pub pattern_type: LifetimePatternType,
277 pub description: String,
278 pub impact: PatternImpact,
279}
280
281#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
282pub enum LifetimePatternType {
283 ManyCaptures,
284 LongLivedClosure,
285 FrequentCreation,
286}
287
288#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
289pub enum PatternImpact {
290 Low,
291 Medium,
292 High,
293}
294
295#[derive(Debug, Clone, Serialize, Deserialize)]
296pub struct ClosureAnalysisReport {
297 pub detected_closures: Vec<DetectedClosure>,
298 pub capture_statistics: CaptureStatistics,
299 pub optimization_suggestions: Vec<OptimizationSuggestion>,
300 pub lifetime_analysis: LifetimeAnalysis,
301 pub analysis_timestamp: u64,
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307
308 #[test]
311 fn test_closure_info() {
312 let closure = ClosureInfo {
313 ptr: 0x1000,
314 captures: vec![],
315 creation_timestamp: 1000,
316 thread_id: "main".to_string(),
317 call_site: "test.rs:10".to_string(),
318 memory_footprint: ClosureFootprint {
319 total_size: 64,
320 capture_count: 0,
321 by_value_count: 0,
322 by_ref_count: 0,
323 by_mut_ref_count: 0,
324 estimated_heap_usage: 0,
325 },
326 optimization_potential: OptimizationPotential {
327 level: OptimizationLevel::None,
328 potential_savings: 0,
329 suggestions: vec![],
330 },
331 };
332
333 assert_eq!(closure.ptr, 0x1000, "Pointer should match");
334 assert_eq!(closure.thread_id, "main", "Thread ID should match");
335 assert_eq!(closure.captures.len(), 0, "Should have no captures");
336 }
337
338 #[test]
341 fn test_capture_info() {
342 let capture = CaptureInfo {
343 var_name: "x".to_string(),
344 var_ptr: 0x2000,
345 mode: CaptureMode::ByReference,
346 var_type: "i32".to_string(),
347 size: 4,
348 lifetime_bound: Some("'a".to_string()),
349 };
350
351 assert_eq!(capture.var_name, "x", "Variable name should match");
352 assert_eq!(
353 capture.mode,
354 CaptureMode::ByReference,
355 "Capture mode should be ByReference"
356 );
357 assert_eq!(capture.size, 4, "Size should be 4");
358 }
359
360 #[test]
363 fn test_capture_mode_variants() {
364 let modes = vec![
365 CaptureMode::ByValue,
366 CaptureMode::ByReference,
367 CaptureMode::ByMutableReference,
368 ];
369
370 for mode in &modes {
371 let debug_str = format!("{mode:?}");
372 assert!(
373 !debug_str.is_empty(),
374 "CaptureMode should have debug representation"
375 );
376 }
377
378 assert_eq!(CaptureMode::ByValue, CaptureMode::ByValue);
379 assert_ne!(CaptureMode::ByValue, CaptureMode::ByReference);
380 }
381
382 #[test]
385 fn test_closure_footprint() {
386 let footprint = ClosureFootprint {
387 total_size: 128,
388 capture_count: 5,
389 by_value_count: 2,
390 by_ref_count: 2,
391 by_mut_ref_count: 1,
392 estimated_heap_usage: 64,
393 };
394
395 assert_eq!(footprint.total_size, 128, "Total size should match");
396 assert_eq!(footprint.capture_count, 5, "Capture count should match");
397 assert_eq!(footprint.by_value_count, 2, "By value count should match");
398 }
399
400 #[test]
403 fn test_optimization_potential() {
404 let potential = OptimizationPotential {
405 level: OptimizationLevel::High,
406 potential_savings: 1024,
407 suggestions: vec!["Use Arc instead of clone".to_string()],
408 };
409
410 assert_eq!(
411 potential.level,
412 OptimizationLevel::High,
413 "Optimization level should be High"
414 );
415 assert_eq!(
416 potential.potential_savings, 1024,
417 "Potential savings should match"
418 );
419 assert_eq!(potential.suggestions.len(), 1, "Should have one suggestion");
420 }
421
422 #[test]
425 fn test_optimization_level_variants() {
426 assert_eq!(OptimizationLevel::None, OptimizationLevel::None);
427 assert_eq!(OptimizationLevel::Low, OptimizationLevel::Low);
428 assert_eq!(OptimizationLevel::Medium, OptimizationLevel::Medium);
429 assert_eq!(OptimizationLevel::High, OptimizationLevel::High);
430
431 assert_ne!(OptimizationLevel::None, OptimizationLevel::High);
432 }
433
434 #[test]
437 fn test_capture_event() {
438 let event = CaptureEvent {
439 closure_ptr: 0x1000,
440 captured_var: CaptureInfo {
441 var_name: "y".to_string(),
442 var_ptr: 0x2000,
443 mode: CaptureMode::ByValue,
444 var_type: "String".to_string(),
445 size: 24,
446 lifetime_bound: None,
447 },
448 event_type: CaptureEventType::Captured,
449 timestamp: 1000,
450 };
451
452 assert_eq!(
453 event.event_type,
454 CaptureEventType::Captured,
455 "Event type should be Captured"
456 );
457 }
458
459 #[test]
462 fn test_capture_event_type_variants() {
463 assert_eq!(CaptureEventType::Captured, CaptureEventType::Captured);
464 assert_eq!(CaptureEventType::Released, CaptureEventType::Released);
465 assert_ne!(CaptureEventType::Captured, CaptureEventType::Released);
466 }
467
468 #[test]
471 fn test_detected_closure() {
472 let detected = DetectedClosure {
473 ptr: 0x1000,
474 type_name: "closure".to_string(),
475 size: 64,
476 estimated_captures: 3,
477 closure_type: ClosureType::FnMut,
478 creation_context: CreationContext {
479 scope_name: Some("test_scope".to_string()),
480 thread_id: "main".to_string(),
481 timestamp: 1000,
482 },
483 memory_impact: MemoryImpact::Medium,
484 };
485
486 assert_eq!(detected.ptr, 0x1000, "Pointer should match");
487 assert_eq!(
488 detected.closure_type,
489 ClosureType::FnMut,
490 "Closure type should be FnMut"
491 );
492 }
493
494 #[test]
497 fn test_closure_type_variants() {
498 let types = vec![
499 ClosureType::Fn,
500 ClosureType::FnMut,
501 ClosureType::FnOnce,
502 ClosureType::Unknown,
503 ];
504
505 for closure_type in &types {
506 let debug_str = format!("{closure_type:?}");
507 assert!(
508 !debug_str.is_empty(),
509 "ClosureType should have debug representation"
510 );
511 }
512 }
513
514 #[test]
517 fn test_memory_impact_variants() {
518 let impacts = vec![
519 MemoryImpact::Minimal,
520 MemoryImpact::Low,
521 MemoryImpact::Medium,
522 MemoryImpact::High,
523 MemoryImpact::VeryHigh,
524 ];
525
526 for impact in &impacts {
527 let debug_str = format!("{impact:?}");
528 assert!(
529 !debug_str.is_empty(),
530 "MemoryImpact should have debug representation"
531 );
532 }
533 }
534
535 #[test]
538 fn test_capture_statistics_default() {
539 let stats = CaptureStatistics::default();
540
541 assert_eq!(stats.total_closures, 0, "Total closures should be 0");
542 assert_eq!(stats.total_captures, 0, "Total captures should be 0");
543 assert_eq!(stats.avg_captures_per_closure, 0.0, "Average should be 0.0");
544 }
545
546 #[test]
549 fn test_capture_statistics_with_values() {
550 let mut captures_by_mode = HashMap::new();
551 captures_by_mode.insert(CaptureMode::ByValue, 10);
552 captures_by_mode.insert(CaptureMode::ByReference, 20);
553
554 let stats = CaptureStatistics {
555 total_closures: 5,
556 total_captures: 30,
557 avg_captures_per_closure: 6.0,
558 total_memory_usage: 1024,
559 captures_by_mode,
560 captures_by_type: HashMap::new(),
561 };
562
563 assert_eq!(stats.total_closures, 5, "Total closures should be 5");
564 assert_eq!(
565 stats.captures_by_mode.get(&CaptureMode::ByValue),
566 Some(&10),
567 "ByValue count should be 10"
568 );
569 }
570
571 #[test]
574 fn test_optimization_suggestion() {
575 let suggestion = OptimizationSuggestion {
576 category: OptimizationCategory::Memory,
577 priority: SuggestionPriority::High,
578 description: "Large closure detected".to_string(),
579 recommendation: "Consider using Arc".to_string(),
580 estimated_impact: "Save 1KB".to_string(),
581 };
582
583 assert_eq!(
584 suggestion.category,
585 OptimizationCategory::Memory,
586 "Category should be Memory"
587 );
588 assert_eq!(
589 suggestion.priority,
590 SuggestionPriority::High,
591 "Priority should be High"
592 );
593 }
594
595 #[test]
598 fn test_optimization_category_variants() {
599 assert_eq!(OptimizationCategory::Memory, OptimizationCategory::Memory);
600 assert_eq!(
601 OptimizationCategory::Performance,
602 OptimizationCategory::Performance
603 );
604 assert_eq!(
605 OptimizationCategory::Lifetime,
606 OptimizationCategory::Lifetime
607 );
608 }
609
610 #[test]
613 fn test_suggestion_priority_variants() {
614 assert_eq!(SuggestionPriority::Low, SuggestionPriority::Low);
615 assert_eq!(SuggestionPriority::Medium, SuggestionPriority::Medium);
616 assert_eq!(SuggestionPriority::High, SuggestionPriority::High);
617 assert_eq!(SuggestionPriority::Critical, SuggestionPriority::Critical);
618 }
619
620 #[test]
623 fn test_lifetime_graph_creation() {
624 let graph = LifetimeGraph::new();
625 assert_eq!(
626 graph.relationships.len(),
627 0,
628 "New graph should have no relationships"
629 );
630 }
631
632 #[test]
635 fn test_lifetime_graph_default() {
636 let graph = LifetimeGraph::default();
637 assert_eq!(
638 graph.relationships.len(),
639 0,
640 "Default graph should have no relationships"
641 );
642 }
643
644 #[test]
647 fn test_lifetime_graph_add_relationships() {
648 let mut graph = LifetimeGraph::new();
649
650 let captures = vec![
651 CaptureInfo {
652 var_name: "x".to_string(),
653 var_ptr: 0x1000,
654 mode: CaptureMode::ByValue,
655 var_type: "i32".to_string(),
656 size: 4,
657 lifetime_bound: None,
658 },
659 CaptureInfo {
660 var_name: "y".to_string(),
661 var_ptr: 0x2000,
662 mode: CaptureMode::ByReference,
663 var_type: "String".to_string(),
664 size: 24,
665 lifetime_bound: None,
666 },
667 ];
668
669 graph.add_closure_relationships(0x5000, &captures);
670
671 assert_eq!(
672 graph.relationships.len(),
673 1,
674 "Should have one closure relationship"
675 );
676 assert_eq!(
677 graph.relationships.get(&0x5000).unwrap().len(),
678 2,
679 "Should have two capture relationships"
680 );
681 }
682
683 #[test]
686 fn test_lifetime_graph_remove_closure() {
687 let mut graph = LifetimeGraph::new();
688
689 let captures = vec![CaptureInfo {
690 var_name: "x".to_string(),
691 var_ptr: 0x1000,
692 mode: CaptureMode::ByValue,
693 var_type: "i32".to_string(),
694 size: 4,
695 lifetime_bound: None,
696 }];
697
698 graph.add_closure_relationships(0x5000, &captures);
699 assert_eq!(graph.relationships.len(), 1, "Should have one relationship");
700
701 graph.remove_closure(0x5000);
702 assert_eq!(
703 graph.relationships.len(),
704 0,
705 "Should have no relationships after removal"
706 );
707 }
708
709 #[test]
712 fn test_lifetime_graph_analyze_mixed_captures() {
713 let mut graph = LifetimeGraph::new();
714
715 let captures = vec![
716 CaptureInfo {
717 var_name: "x".to_string(),
718 var_ptr: 0x1000,
719 mode: CaptureMode::ByValue,
720 var_type: "i32".to_string(),
721 size: 4,
722 lifetime_bound: None,
723 },
724 CaptureInfo {
725 var_name: "y".to_string(),
726 var_ptr: 0x2000,
727 mode: CaptureMode::ByReference,
728 var_type: "String".to_string(),
729 size: 24,
730 lifetime_bound: None,
731 },
732 ];
733
734 graph.add_closure_relationships(0x5000, &captures);
735 let analysis = graph.analyze_lifetimes();
736
737 assert_eq!(
738 analysis.total_relationships, 1,
739 "Should have one relationship"
740 );
741 assert!(
742 analysis
743 .potential_issues
744 .iter()
745 .any(|i| i.issue_type == LifetimeIssueType::MixedCaptureMode),
746 "Should detect mixed capture mode issue"
747 );
748 }
749
750 #[test]
753 fn test_lifetime_graph_analyze_many_captures() {
754 let mut graph = LifetimeGraph::new();
755
756 let captures: Vec<CaptureInfo> = (0..8)
757 .map(|i| CaptureInfo {
758 var_name: format!("var{}", i),
759 var_ptr: 0x1000 + i,
760 mode: CaptureMode::ByReference,
761 var_type: "i32".to_string(),
762 size: 4,
763 lifetime_bound: None,
764 })
765 .collect();
766
767 graph.add_closure_relationships(0x5000, &captures);
768 let analysis = graph.analyze_lifetimes();
769
770 assert!(
771 analysis
772 .lifetime_patterns
773 .iter()
774 .any(|p| p.pattern_type == LifetimePatternType::ManyCaptures),
775 "Should detect many captures pattern"
776 );
777 }
778
779 #[test]
782 fn test_relationship_type_classification() {
783 let captures = vec![
784 CaptureInfo {
785 var_name: "v1".to_string(),
786 var_ptr: 0x1000,
787 mode: CaptureMode::ByValue,
788 var_type: "i32".to_string(),
789 size: 4,
790 lifetime_bound: None,
791 },
792 CaptureInfo {
793 var_name: "v2".to_string(),
794 var_ptr: 0x2000,
795 mode: CaptureMode::ByReference,
796 var_type: "i32".to_string(),
797 size: 4,
798 lifetime_bound: None,
799 },
800 CaptureInfo {
801 var_name: "v3".to_string(),
802 var_ptr: 0x3000,
803 mode: CaptureMode::ByMutableReference,
804 var_type: "i32".to_string(),
805 size: 4,
806 lifetime_bound: None,
807 },
808 ];
809
810 let mut test_graph = LifetimeGraph::new();
811 test_graph.add_closure_relationships(0x5000, &captures);
812
813 let rels = test_graph.relationships.get(&0x5000).unwrap();
814 assert_eq!(
815 rels[0].relationship_type,
816 RelationshipType::Ownership,
817 "ByValue should be Ownership"
818 );
819 assert_eq!(
820 rels[1].relationship_type,
821 RelationshipType::SharedBorrow,
822 "ByReference should be SharedBorrow"
823 );
824 assert_eq!(
825 rels[2].relationship_type,
826 RelationshipType::ExclusiveBorrow,
827 "ByMutableReference should be ExclusiveBorrow"
828 );
829 }
830
831 #[test]
834 fn test_lifetime_issue() {
835 let issue = LifetimeIssue {
836 closure_ptr: 0x1000,
837 issue_type: LifetimeIssueType::PotentialDanglingReference,
838 description: "Potential dangling reference".to_string(),
839 severity: IssueSeverity::High,
840 suggestion: "Check lifetime bounds".to_string(),
841 };
842
843 assert_eq!(
844 issue.issue_type,
845 LifetimeIssueType::PotentialDanglingReference,
846 "Issue type should match"
847 );
848 assert_eq!(
849 issue.severity,
850 IssueSeverity::High,
851 "Severity should be High"
852 );
853 }
854
855 #[test]
858 fn test_lifetime_pattern() {
859 let pattern = LifetimePattern {
860 pattern_type: LifetimePatternType::LongLivedClosure,
861 description: "Closure lives for entire program".to_string(),
862 impact: PatternImpact::High,
863 };
864
865 assert_eq!(
866 pattern.pattern_type,
867 LifetimePatternType::LongLivedClosure,
868 "Pattern type should match"
869 );
870 assert_eq!(pattern.impact, PatternImpact::High, "Impact should be High");
871 }
872
873 #[test]
876 fn test_closure_analysis_report() {
877 let report = ClosureAnalysisReport {
878 detected_closures: vec![],
879 capture_statistics: CaptureStatistics::default(),
880 optimization_suggestions: vec![],
881 lifetime_analysis: LifetimeAnalysis::default(),
882 analysis_timestamp: 1000,
883 };
884
885 assert_eq!(
886 report.detected_closures.len(),
887 0,
888 "Should have no detected closures"
889 );
890 assert_eq!(report.analysis_timestamp, 1000, "Timestamp should match");
891 }
892
893 #[test]
896 fn test_capture_mode_serialization() {
897 let mode = CaptureMode::ByMutableReference;
898 let json = serde_json::to_string(&mode);
899 assert!(json.is_ok(), "Should serialize to JSON");
900
901 let deserialized: Result<CaptureMode, _> = serde_json::from_str(&json.unwrap());
902 assert!(deserialized.is_ok(), "Should deserialize from JSON");
903 assert_eq!(
904 deserialized.unwrap(),
905 CaptureMode::ByMutableReference,
906 "Should preserve value"
907 );
908 }
909
910 #[test]
913 fn test_optimization_level_serialization() {
914 let level = OptimizationLevel::Medium;
915 let json = serde_json::to_string(&level);
916 assert!(json.is_ok(), "Should serialize to JSON");
917
918 let deserialized: Result<OptimizationLevel, _> = serde_json::from_str(&json.unwrap());
919 assert!(deserialized.is_ok(), "Should deserialize from JSON");
920 assert_eq!(
921 deserialized.unwrap(),
922 OptimizationLevel::Medium,
923 "Should preserve value"
924 );
925 }
926}