debtmap/refactoring/
mod.rs

1use crate::core::{FileMetrics, FunctionMetrics};
2use std::sync::Arc;
3
4pub mod guidance;
5pub mod opportunities;
6pub mod patterns;
7
8pub use guidance::*;
9pub use opportunities::*;
10pub use patterns::*;
11
12#[derive(Debug, Clone)]
13pub struct RefactoringAnalysis {
14    pub function_name: String,
15    pub function_role: FunctionRole,
16    pub detected_patterns: Vec<DetectedPattern>,
17    pub refactoring_opportunities: Vec<RefactoringOpportunity>,
18    pub quality_assessment: QualityAssessment,
19    pub recommendations: Vec<Recommendation>,
20}
21
22#[derive(Debug, Clone)]
23pub enum FunctionRole {
24    PureLogic {
25        complexity_tolerance: u32,
26        testing_expectation: TestingExpectation,
27    },
28    IOOrchestrator {
29        expected_patterns: Vec<OrchestrationPattern>,
30        complexity_tolerance: u32,
31    },
32    FormattingFunction {
33        input_types: Vec<String>,
34        output_type: String,
35        testability_importance: TestabilityImportance,
36    },
37    TraitImplementation {
38        trait_name: String,
39        testing_strategy: TraitTestingStrategy,
40    },
41    FrameworkCallback {
42        framework: String,
43        callback_type: CallbackType,
44    },
45}
46
47#[derive(Debug, Clone)]
48pub enum TestingExpectation {
49    HighCoverage,
50    ModerateCoverage,
51    LowCoverage,
52}
53
54#[derive(Debug, Clone)]
55pub enum TestabilityImportance {
56    Critical,
57    High,
58    Medium,
59    Low,
60}
61
62#[derive(Debug, Clone)]
63pub enum TraitTestingStrategy {
64    TestThroughCallers,
65    DirectUnitTests,
66    IntegrationTests,
67}
68
69#[derive(Debug, Clone)]
70pub enum CallbackType {
71    EventHandler,
72    Lifecycle,
73    DataTransform,
74}
75
76#[derive(Debug, Clone)]
77pub struct OrchestrationPattern {
78    pub pattern_type: String,
79    pub description: String,
80}
81
82#[derive(Debug, Clone)]
83pub struct DetectedPattern {
84    pub pattern_type: PatternType,
85    pub confidence: f64,
86    pub evidence: PatternEvidence,
87    pub assessment: PatternAssessment,
88}
89
90#[derive(Debug, Clone)]
91pub enum PatternType {
92    IOOrchestration(OrchestrationPattern),
93    PureFormatting(FormattingPattern),
94    MixedConcerns(ConcernMixingPattern),
95    TraitImplementation(TraitPattern),
96    TestFunction(TestPattern),
97    FunctionalComposition,
98    ImperativeLoop,
99    MutableState,
100    SideEffects,
101}
102
103#[derive(Debug, Clone)]
104pub struct FormattingPattern {
105    pub format_type: String,
106    pub complexity: u32,
107}
108
109#[derive(Debug, Clone)]
110pub struct ConcernMixingPattern {
111    pub concerns: Vec<String>,
112    pub separation_difficulty: SeparationDifficulty,
113}
114
115#[derive(Debug, Clone)]
116pub enum SeparationDifficulty {
117    Trivial,
118    Low,
119    Medium,
120    High,
121    VeryHigh,
122}
123
124#[derive(Debug, Clone)]
125pub struct TraitPattern {
126    pub trait_name: String,
127    pub method_name: String,
128}
129
130#[derive(Debug, Clone)]
131pub struct TestPattern {
132    pub test_type: String,
133    pub framework: String,
134}
135
136#[derive(Debug, Clone)]
137pub struct PatternEvidence {
138    pub code_snippets: Vec<String>,
139    pub line_numbers: Vec<u32>,
140    pub confidence_factors: Vec<String>,
141}
142
143#[derive(Debug, Clone)]
144pub enum PatternAssessment {
145    GoodExample {
146        strengths: Vec<String>,
147        why_good: String,
148    },
149    ImprovementOpportunity {
150        current_issues: Vec<String>,
151        potential_benefits: Vec<String>,
152        refactoring_suggestions: Vec<RefactoringOpportunity>,
153    },
154    AntiPattern {
155        problems: Vec<String>,
156        recommended_patterns: Vec<PatternType>,
157        urgency: Urgency,
158    },
159}
160
161#[derive(Debug, Clone)]
162pub enum Urgency {
163    Low,
164    Medium,
165    High,
166    Critical,
167}
168
169#[derive(Debug, Clone)]
170pub struct QualityAssessment {
171    pub overall_score: f64,
172    pub strengths: Vec<String>,
173    pub improvement_areas: Vec<String>,
174    pub pattern_compliance: f64,
175    pub role_appropriateness: f64,
176}
177
178#[derive(Debug, Clone)]
179pub struct Recommendation {
180    pub title: String,
181    pub description: String,
182    pub priority: Priority,
183    pub effort_estimate: EffortEstimate,
184    pub benefits: Vec<String>,
185    pub example: Option<RefactoringExample>,
186}
187
188#[derive(Debug, Clone)]
189pub enum Priority {
190    Low,
191    Medium,
192    High,
193    Critical,
194}
195
196#[derive(Debug, Clone)]
197pub struct RefactoringExample {
198    pub before: String,
199    pub after: String,
200    pub explanation: String,
201}
202
203#[derive(Debug, Clone)]
204pub enum EffortEstimate {
205    Trivial,     // < 15 minutes
206    Low,         // 15-60 minutes
207    Medium,      // 1-4 hours
208    High,        // 4-8 hours
209    Significant, // > 8 hours
210}
211
212pub struct PatternRecognitionEngine {
213    pattern_matchers: Vec<Arc<dyn PatternMatcher>>,
214    function_classifier: FunctionRoleClassifier,
215    refactoring_advisor: RefactoringAdvisor,
216}
217
218impl Default for PatternRecognitionEngine {
219    fn default() -> Self {
220        Self {
221            pattern_matchers: patterns::create_pattern_matchers(),
222            function_classifier: FunctionRoleClassifier::default(),
223            refactoring_advisor: RefactoringAdvisor::default(),
224        }
225    }
226}
227
228impl PatternRecognitionEngine {
229    pub fn new() -> Self {
230        Self::default()
231    }
232
233    pub fn analyze_function(
234        &self,
235        function: &FunctionMetrics,
236        file: &FileMetrics,
237    ) -> RefactoringAnalysis {
238        let role = self.function_classifier.classify(function, file);
239        let patterns = self.identify_patterns(function, file);
240        let opportunities = self
241            .refactoring_advisor
242            .find_opportunities(function, file, &role, &patterns);
243        let quality = self.assess_quality(function, &patterns, &role);
244        let recommendations = self.generate_recommendations(&opportunities, &quality);
245
246        RefactoringAnalysis {
247            function_name: function.name.clone(),
248            function_role: role,
249            detected_patterns: patterns,
250            refactoring_opportunities: opportunities,
251            quality_assessment: quality,
252            recommendations,
253        }
254    }
255
256    fn identify_patterns(
257        &self,
258        function: &FunctionMetrics,
259        file: &FileMetrics,
260    ) -> Vec<DetectedPattern> {
261        self.pattern_matchers
262            .iter()
263            .filter_map(|matcher| matcher.match_pattern(function, file))
264            .collect()
265    }
266
267    fn assess_quality(
268        &self,
269        function: &FunctionMetrics,
270        patterns: &[DetectedPattern],
271        role: &FunctionRole,
272    ) -> QualityAssessment {
273        let mut strengths = Vec::new();
274        let mut improvement_areas = Vec::new();
275        let mut pattern_score = 0.0;
276        let mut pattern_count = 0.0;
277
278        for pattern in patterns {
279            pattern_count += 1.0;
280            match &pattern.assessment {
281                PatternAssessment::GoodExample { strengths: s, .. } => {
282                    strengths.extend(s.clone());
283                    pattern_score += 1.0;
284                }
285                PatternAssessment::ImprovementOpportunity { current_issues, .. } => {
286                    improvement_areas.extend(current_issues.clone());
287                    pattern_score += 0.5;
288                }
289                PatternAssessment::AntiPattern { problems, .. } => {
290                    improvement_areas.extend(problems.clone());
291                }
292            }
293        }
294
295        let pattern_compliance = if pattern_count > 0.0 {
296            pattern_score / pattern_count
297        } else {
298            1.0
299        };
300
301        let role_appropriateness = self.calculate_role_appropriateness(function, role);
302        let overall_score = (pattern_compliance + role_appropriateness) / 2.0;
303
304        QualityAssessment {
305            overall_score,
306            strengths,
307            improvement_areas,
308            pattern_compliance,
309            role_appropriateness,
310        }
311    }
312
313    fn calculate_role_appropriateness(
314        &self,
315        function: &FunctionMetrics,
316        role: &FunctionRole,
317    ) -> f64 {
318        match role {
319            FunctionRole::PureLogic {
320                complexity_tolerance,
321                ..
322            } => {
323                if function.cyclomatic <= *complexity_tolerance {
324                    1.0
325                } else {
326                    0.5
327                }
328            }
329            FunctionRole::IOOrchestrator {
330                complexity_tolerance,
331                ..
332            } => {
333                if function.cyclomatic <= *complexity_tolerance {
334                    1.0
335                } else {
336                    0.7
337                }
338            }
339            _ => 0.8,
340        }
341    }
342
343    /// Format patterns into a comma-separated string
344    fn format_patterns<T: std::fmt::Debug>(patterns: &[T]) -> String {
345        patterns
346            .iter()
347            .map(|p| format!("{:?}", p))
348            .collect::<Vec<_>>()
349            .join(", ")
350    }
351
352    /// Map complexity level to priority
353    fn complexity_to_priority(complexity: &ComplexityLevel) -> Priority {
354        match complexity {
355            ComplexityLevel::Severe => Priority::Critical,
356            ComplexityLevel::High => Priority::High,
357            ComplexityLevel::Moderate => Priority::Medium,
358            _ => Priority::Low,
359        }
360    }
361
362    fn generate_recommendations(
363        &self,
364        opportunities: &[RefactoringOpportunity],
365        _quality: &QualityAssessment,
366    ) -> Vec<Recommendation> {
367        let mut recommendations = Vec::new();
368
369        for opportunity in opportunities {
370            let recommendation = match opportunity {
371                RefactoringOpportunity::ExtractPureFunctions {
372                    source_function,
373                    complexity_level,
374                    extraction_strategy: _,
375                    suggested_functions,
376                    functional_patterns,
377                    benefits,
378                    effort_estimate,
379                    example,
380                } => {
381                    let description = match complexity_level {
382                        ComplexityLevel::Moderate => {
383                            format!(
384                                "Extract {} pure functions using direct functional transformation. \
385                                Apply patterns: {}",
386                                suggested_functions.len(),
387                                Self::format_patterns(functional_patterns)
388                            )
389                        }
390                        ComplexityLevel::High => {
391                            format!(
392                                "Extract {} pure functions using decompose-then-transform strategy. \
393                                First decompose into logical units, then apply functional patterns.",
394                                suggested_functions.len()
395                            )
396                        }
397                        ComplexityLevel::Severe => {
398                            format!(
399                                "Architectural refactoring needed. Extract {} pure functions into modules \
400                                and design functional core with imperative shell.",
401                                suggested_functions.len()
402                            )
403                        }
404                        _ => continue,
405                    };
406
407                    Recommendation {
408                        title: format!("Extract pure functions from {}", source_function),
409                        description,
410                        priority: Self::complexity_to_priority(complexity_level),
411                        effort_estimate: effort_estimate.clone(),
412                        benefits: benefits.clone(),
413                        example: example.as_ref().map(|e| RefactoringExample {
414                            before: e.before_imperative.clone(),
415                            after: e.after_functional.clone(),
416                            explanation: Self::format_patterns(&e.patterns_applied),
417                        }),
418                    }
419                }
420                RefactoringOpportunity::ConvertToFunctionalStyle {
421                    imperative_function,
422                    target_patterns,
423                    benefits,
424                    effort_estimate,
425                    ..
426                } => Recommendation {
427                    title: format!("Convert {} to functional style", imperative_function),
428                    description: format!(
429                        "Apply functional patterns: {}",
430                        Self::format_patterns(target_patterns)
431                    ),
432                    priority: Priority::Medium,
433                    effort_estimate: effort_estimate.clone(),
434                    benefits: benefits.clone(),
435                    example: None,
436                },
437                RefactoringOpportunity::ExtractSideEffects {
438                    mixed_function,
439                    pure_core,
440                    benefits,
441                    effort_estimate,
442                    ..
443                } => Recommendation {
444                    title: format!("Extract side effects from {}", mixed_function),
445                    description: format!(
446                        "Create pure function '{}' and move I/O to boundaries",
447                        pure_core.name
448                    ),
449                    priority: Priority::High,
450                    effort_estimate: effort_estimate.clone(),
451                    benefits: benefits.clone(),
452                    example: None,
453                },
454            };
455            recommendations.push(recommendation);
456        }
457
458        recommendations
459    }
460}
461
462pub struct FunctionRoleClassifier {
463    io_detectors: Vec<Arc<dyn IoDetector>>,
464    formatting_detectors: Vec<Arc<dyn FormattingDetector>>,
465    trait_analyzers: Vec<Arc<dyn TraitAnalyzer>>,
466}
467
468impl Default for FunctionRoleClassifier {
469    fn default() -> Self {
470        Self {
471            io_detectors: patterns::create_io_detectors(),
472            formatting_detectors: patterns::create_formatting_detectors(),
473            trait_analyzers: patterns::create_trait_analyzers(),
474        }
475    }
476}
477
478impl FunctionRoleClassifier {
479    pub fn new() -> Self {
480        Self::default()
481    }
482
483    pub fn classify(&self, function: &FunctionMetrics, file: &FileMetrics) -> FunctionRole {
484        // Check for trait implementation first
485        for analyzer in &self.trait_analyzers {
486            if let Some(trait_info) = analyzer.detect_trait_implementation(function, file) {
487                return FunctionRole::TraitImplementation {
488                    trait_name: trait_info.trait_name,
489                    testing_strategy: TraitTestingStrategy::TestThroughCallers,
490                };
491            }
492        }
493
494        // Check for IO orchestration
495        for detector in &self.io_detectors {
496            if let Some(io_info) = detector.detect_io_orchestration(function, file) {
497                return FunctionRole::IOOrchestrator {
498                    expected_patterns: io_info.patterns,
499                    complexity_tolerance: 5,
500                };
501            }
502        }
503
504        // Check for formatting function
505        for detector in &self.formatting_detectors {
506            if let Some(formatting_info) = detector.detect_formatting_function(function, file) {
507                return FunctionRole::FormattingFunction {
508                    input_types: formatting_info.inputs,
509                    output_type: formatting_info.output,
510                    testability_importance: TestabilityImportance::High,
511                };
512            }
513        }
514
515        // Default to pure logic with strict expectations
516        FunctionRole::PureLogic {
517            complexity_tolerance: 3,
518            testing_expectation: TestingExpectation::HighCoverage,
519        }
520    }
521}
522
523pub struct RefactoringAdvisor {
524    opportunity_detectors: Vec<Arc<dyn RefactoringDetector>>,
525}
526
527impl Default for RefactoringAdvisor {
528    fn default() -> Self {
529        Self {
530            opportunity_detectors: opportunities::create_refactoring_detectors(),
531        }
532    }
533}
534
535impl RefactoringAdvisor {
536    pub fn new() -> Self {
537        Self::default()
538    }
539
540    pub fn find_opportunities(
541        &self,
542        function: &FunctionMetrics,
543        file: &FileMetrics,
544        role: &FunctionRole,
545        patterns: &[DetectedPattern],
546    ) -> Vec<RefactoringOpportunity> {
547        let mut opportunities = Vec::new();
548
549        for detector in &self.opportunity_detectors {
550            opportunities.extend(detector.detect_opportunities(function, file, role, patterns));
551        }
552
553        // Sort by priority
554        opportunities.sort_by_key(|o| match o {
555            RefactoringOpportunity::ExtractPureFunctions {
556                complexity_level, ..
557            } => match complexity_level {
558                ComplexityLevel::Severe => 0,
559                ComplexityLevel::High => 1,
560                ComplexityLevel::Moderate => 2,
561                ComplexityLevel::Low => 3,
562            },
563            RefactoringOpportunity::ExtractSideEffects { .. } => 1,
564            RefactoringOpportunity::ConvertToFunctionalStyle { .. } => 2,
565        });
566
567        opportunities
568    }
569}
570
571// Trait definitions for extensibility
572pub trait PatternMatcher: Send + Sync {
573    fn match_pattern(
574        &self,
575        function: &FunctionMetrics,
576        file: &FileMetrics,
577    ) -> Option<DetectedPattern>;
578}
579
580pub trait IoDetector: Send + Sync {
581    fn detect_io_orchestration(
582        &self,
583        function: &FunctionMetrics,
584        file: &FileMetrics,
585    ) -> Option<IoInfo>;
586}
587
588pub trait FormattingDetector: Send + Sync {
589    fn detect_formatting_function(
590        &self,
591        function: &FunctionMetrics,
592        file: &FileMetrics,
593    ) -> Option<FormattingInfo>;
594}
595
596pub trait TraitAnalyzer: Send + Sync {
597    fn detect_trait_implementation(
598        &self,
599        function: &FunctionMetrics,
600        file: &FileMetrics,
601    ) -> Option<TraitInfo>;
602}
603
604pub trait RefactoringDetector: Send + Sync {
605    fn detect_opportunities(
606        &self,
607        function: &FunctionMetrics,
608        file: &FileMetrics,
609        role: &FunctionRole,
610        patterns: &[DetectedPattern],
611    ) -> Vec<RefactoringOpportunity>;
612    fn priority(&self) -> Priority;
613}
614
615#[derive(Debug, Clone)]
616pub struct IoInfo {
617    pub patterns: Vec<OrchestrationPattern>,
618    pub io_operations: Vec<String>,
619}
620
621#[derive(Debug, Clone)]
622pub struct FormattingInfo {
623    pub inputs: Vec<String>,
624    pub output: String,
625    pub format_type: String,
626}
627
628#[derive(Debug, Clone)]
629pub struct TraitInfo {
630    pub trait_name: String,
631    pub method_name: String,
632}
633
634#[derive(Debug, Clone)]
635pub enum RefactoringOpportunity {
636    ExtractPureFunctions {
637        source_function: String,
638        complexity_level: ComplexityLevel,
639        extraction_strategy: ExtractionStrategy,
640        suggested_functions: Vec<PureFunctionSpec>,
641        functional_patterns: Vec<FunctionalPattern>,
642        benefits: Vec<String>,
643        effort_estimate: EffortEstimate,
644        example: Option<FunctionalTransformExample>,
645    },
646    ConvertToFunctionalStyle {
647        imperative_function: String,
648        current_patterns: Vec<ImperativePattern>,
649        target_patterns: Vec<FunctionalPattern>,
650        transformation_steps: Vec<TransformationStep>,
651        benefits: Vec<String>,
652        effort_estimate: EffortEstimate,
653    },
654    ExtractSideEffects {
655        mixed_function: String,
656        pure_core: PureFunctionSpec,
657        io_shell: IoShellSpec,
658        benefits: Vec<String>,
659        effort_estimate: EffortEstimate,
660    },
661}
662
663#[derive(Debug, Clone, PartialEq)]
664pub enum ComplexityLevel {
665    Low,      // ≤5 - No action needed
666    Moderate, // 6-10 - Direct functional transformation
667    High,     // 11-15 - Decompose then transform
668    Severe,   // >15 - Architectural refactoring
669}
670
671#[derive(Debug, Clone)]
672pub enum ExtractionStrategy {
673    DirectFunctionalTransformation {
674        patterns_to_apply: Vec<FunctionalPattern>,
675        functions_to_extract: u32,
676    },
677    DecomposeAndTransform {
678        decomposition_steps: Vec<String>,
679        functions_to_extract: u32,
680        then_apply_patterns: Vec<FunctionalPattern>,
681    },
682    ArchitecturalRefactoring {
683        extract_modules: Vec<String>,
684        pure_core_functions: Vec<PureFunctionSpec>,
685        design_imperative_shell: IoShellSpec,
686    },
687}
688
689#[derive(Debug, Clone)]
690pub struct PureFunctionSpec {
691    pub name: String,
692    pub inputs: Vec<String>,
693    pub output: String,
694    pub purpose: String,
695    pub no_side_effects: bool,
696    pub testability: TestabilityLevel,
697}
698
699#[derive(Debug, Clone)]
700pub enum TestabilityLevel {
701    Trivial,
702    Easy,
703    Moderate,
704    Hard,
705}
706
707#[derive(Debug, Clone)]
708pub struct IoShellSpec {
709    pub name: String,
710    pub io_operations: Vec<String>,
711    pub delegates_to: Vec<String>,
712}
713
714#[derive(Debug, Clone)]
715pub enum FunctionalPattern {
716    MapOverLoop,
717    FilterPredicate,
718    FoldAccumulation,
719    PatternMatchOverIfElse,
720    ComposeFunctions,
721    PartialApplication,
722    Monadic(MonadicPattern),
723    Pipeline,
724    Recursion,
725}
726
727#[derive(Debug, Clone)]
728pub enum MonadicPattern {
729    Option,
730    Result,
731    Future,
732    State,
733}
734
735#[derive(Debug, Clone)]
736pub enum ImperativePattern {
737    MutableLoop,
738    StateModification,
739    NestedConditions,
740    SideEffectMixing,
741}
742
743#[derive(Debug, Clone)]
744pub struct TransformationStep {
745    pub description: String,
746    pub pattern_applied: FunctionalPattern,
747}
748
749#[derive(Debug, Clone)]
750pub struct FunctionalTransformExample {
751    pub before_imperative: String,
752    pub after_functional: String,
753    pub patterns_applied: Vec<FunctionalPattern>,
754    pub benefits_demonstrated: Vec<String>,
755}