Skip to main content

debtmap/refactoring/guidance/
mod.rs

1use crate::refactoring::{
2    ComplexityLevel, EffortEstimate, FunctionalPattern, Priority, QualityAssessment,
3    Recommendation, RefactoringAnalysis, RefactoringExample, RefactoringOpportunity,
4};
5
6#[derive(Default)]
7pub struct RefactoringGuidanceGenerator;
8
9impl RefactoringGuidanceGenerator {
10    pub fn new() -> Self {
11        Self
12    }
13
14    /// Classify pattern category for collection transformations
15    fn is_collection_pattern(pattern: &FunctionalPattern) -> Option<&'static str> {
16        match pattern {
17            FunctionalPattern::MapOverLoop => Some("Replace loops with map"),
18            FunctionalPattern::FilterPredicate => Some("Extract filter predicates"),
19            FunctionalPattern::FoldAccumulation => Some("Use fold for aggregation"),
20            _ => None,
21        }
22    }
23
24    /// Classify pattern category for control flow patterns
25    fn is_control_flow_pattern(pattern: &FunctionalPattern) -> Option<&'static str> {
26        match pattern {
27            FunctionalPattern::PatternMatchOverIfElse => Some("Pattern matching"),
28            FunctionalPattern::Recursion => Some("Recursion"),
29            _ => None,
30        }
31    }
32
33    /// Classify pattern category for composition patterns
34    fn is_composition_pattern(pattern: &FunctionalPattern) -> Option<&'static str> {
35        match pattern {
36            FunctionalPattern::ComposeFunctions => Some("Compose functions"),
37            FunctionalPattern::PartialApplication => Some("Partial application"),
38            FunctionalPattern::Pipeline => Some("Function pipeline"),
39            _ => None,
40        }
41    }
42
43    fn format_complexity_level(level: &ComplexityLevel) -> &'static str {
44        match level {
45            ComplexityLevel::Low => "LOW",
46            ComplexityLevel::Moderate => "MODERATE",
47            ComplexityLevel::High => "HIGH",
48            ComplexityLevel::Severe => "SEVERE",
49        }
50    }
51
52    fn format_refactoring_strategy(level: &ComplexityLevel) -> &'static str {
53        match level {
54            ComplexityLevel::Moderate => "direct functional transformation",
55            ComplexityLevel::High => "decompose-then-transform",
56            ComplexityLevel::Severe => "architectural refactoring",
57            _ => "no",
58        }
59    }
60
61    fn format_benefits_list(benefits: &[String]) -> String {
62        if benefits.is_empty() {
63            String::new()
64        } else {
65            let mut output = String::from("BENEFITS:\n");
66            for benefit in benefits {
67                output.push_str(&format!("  • {}\n", benefit));
68            }
69            output
70        }
71    }
72
73    fn get_priority_icon(priority: &Priority) -> &'static str {
74        match priority {
75            Priority::Critical => "[ERROR]",
76            Priority::High => "[WARN]",
77            Priority::Medium => "[WARN]",
78            Priority::Low => "[OK]",
79        }
80    }
81
82    fn get_effort_string(effort: &EffortEstimate) -> &'static str {
83        match effort {
84            EffortEstimate::Trivial => "< 15 min",
85            EffortEstimate::Low => "15-60 min",
86            EffortEstimate::Medium => "1-4 hours",
87            EffortEstimate::High => "4-8 hours",
88            EffortEstimate::Significant => "> 8 hours",
89        }
90    }
91
92    pub fn generate_guidance(&self, analysis: &RefactoringAnalysis) -> String {
93        let mut output = String::new();
94
95        // Add header based on quality assessment
96        if analysis.quality_assessment.overall_score > 0.8 {
97            output.push_str(&format!("[OK] Good Example: {}\n", analysis.function_name));
98            output.push_str(&self.format_strengths(&analysis.quality_assessment));
99        } else {
100            output.push_str(&format!(
101                "[PERF] Refactoring Opportunity: {}\n",
102                analysis.function_name
103            ));
104            output.push_str(&self.format_opportunities(&analysis.refactoring_opportunities));
105        }
106
107        // Add recommendations
108        if !analysis.recommendations.is_empty() {
109            output.push_str("\n## Recommendations\n\n");
110            for rec in &analysis.recommendations {
111                output.push_str(&self.format_recommendation(rec));
112            }
113        }
114
115        output
116    }
117
118    fn format_strengths(&self, quality: &QualityAssessment) -> String {
119        let mut output = String::new();
120
121        if !quality.strengths.is_empty() {
122            output.push_str("Strengths:\n");
123            for strength in &quality.strengths {
124                output.push_str(&format!("  • {}\n", strength));
125            }
126        }
127
128        output
129    }
130
131    fn format_opportunities(&self, opportunities: &[RefactoringOpportunity]) -> String {
132        let mut output = String::new();
133
134        for opportunity in opportunities {
135            match opportunity {
136                RefactoringOpportunity::ExtractPureFunctions {
137                    complexity_level,
138                    suggested_functions,
139                    functional_patterns,
140                    benefits,
141                    ..
142                } => {
143                    output.push_str(&format!(
144                        "\n{} Complexity Detected\n",
145                        Self::format_complexity_level(complexity_level)
146                    ));
147
148                    output.push_str(&format!(
149                        "ACTION: Extract {} pure functions using {} strategy\n",
150                        suggested_functions.len(),
151                        Self::format_refactoring_strategy(complexity_level)
152                    ));
153
154                    if !functional_patterns.is_empty() {
155                        output.push_str("PATTERNS: ");
156                        let patterns: Vec<String> = functional_patterns
157                            .iter()
158                            .map(|p| self.pattern_to_string(p))
159                            .collect();
160                        output.push_str(&patterns.join(", "));
161                        output.push('\n');
162                    }
163
164                    output.push_str(&Self::format_benefits_list(benefits));
165                }
166                RefactoringOpportunity::ConvertToFunctionalStyle {
167                    target_patterns,
168                    benefits,
169                    ..
170                } => {
171                    output.push_str("\nFunctional Transformation Opportunity\n");
172                    output.push_str("ACTION: Apply functional patterns: ");
173                    let patterns: Vec<String> = target_patterns
174                        .iter()
175                        .map(|p| self.pattern_to_string(p))
176                        .collect();
177                    output.push_str(&patterns.join(", "));
178                    output.push('\n');
179
180                    output.push_str(&Self::format_benefits_list(benefits));
181                }
182                RefactoringOpportunity::ExtractSideEffects {
183                    pure_core,
184                    benefits,
185                    ..
186                } => {
187                    output.push_str("\nSide Effect Extraction Needed\n");
188                    output.push_str(&format!(
189                        "ACTION: Extract pure function '{}' and move I/O to boundaries\n",
190                        pure_core.name
191                    ));
192
193                    output.push_str(&Self::format_benefits_list(benefits));
194                }
195            }
196        }
197
198        output
199    }
200
201    fn format_recommendation(&self, rec: &Recommendation) -> String {
202        let mut output = String::new();
203
204        let priority_icon = Self::get_priority_icon(&rec.priority);
205        let effort_str = Self::get_effort_string(&rec.effort_estimate);
206
207        output.push_str(&format!(
208            "{} {} [Effort: {}]\n",
209            priority_icon, rec.title, effort_str
210        ));
211        output.push_str(&format!("   {}\n", rec.description));
212
213        if let Some(example) = &rec.example {
214            output.push_str(&self.format_example(example));
215        }
216
217        output.push('\n');
218        output
219    }
220
221    fn format_example(&self, example: &RefactoringExample) -> String {
222        let mut output = String::new();
223
224        output.push_str("\n   Example:\n");
225        output.push_str("   Before:\n");
226        for line in example.before.lines() {
227            output.push_str(&format!("     {}\n", line));
228        }
229        output.push_str("   After:\n");
230        for line in example.after.lines() {
231            output.push_str(&format!("     {}\n", line));
232        }
233        if !example.explanation.is_empty() {
234            output.push_str(&format!("   Patterns Applied: {}\n", example.explanation));
235        }
236
237        output
238    }
239
240    fn monadic_pattern_to_str(pattern: &crate::refactoring::MonadicPattern) -> &'static str {
241        match pattern {
242            crate::refactoring::MonadicPattern::Option => "Option monad",
243            crate::refactoring::MonadicPattern::Result => "Result monad",
244            crate::refactoring::MonadicPattern::Future => "Future monad",
245            crate::refactoring::MonadicPattern::State => "State monad",
246        }
247    }
248
249    fn pattern_to_string(&self, pattern: &FunctionalPattern) -> String {
250        if let Some(desc) = Self::is_collection_pattern(pattern) {
251            return desc.to_string();
252        }
253        if let Some(desc) = Self::is_control_flow_pattern(pattern) {
254            return desc.to_string();
255        }
256        if let Some(desc) = Self::is_composition_pattern(pattern) {
257            return desc.to_string();
258        }
259        if let FunctionalPattern::Monadic(m) = pattern {
260            return Self::monadic_pattern_to_str(m).to_string();
261        }
262        "Unknown pattern".to_string()
263    }
264}
265
266pub struct EducationalContentGenerator;
267
268impl EducationalContentGenerator {
269    pub fn generate_functional_programming_tips() -> Vec<String> {
270        vec![
271            "[TIP] Pure functions have no side effects and always return the same output for the same input".to_string(),
272            "[TIP] Use map() to transform collections instead of for loops with push()".to_string(),
273            "[TIP] Use filter() to select items instead of if statements in loops".to_string(),
274            "[TIP] Use fold() to aggregate values instead of mutable accumulators".to_string(),
275            "[TIP] Keep I/O operations at the boundaries of your application".to_string(),
276            "[TIP] Compose small, focused functions to build complex behavior".to_string(),
277            "[TIP] Prefer immutable data structures to prevent unexpected mutations".to_string(),
278            "[TIP] Use Result<T, E> for error handling instead of exceptions".to_string(),
279            "[TIP] Pattern matching is more expressive than if-else chains".to_string(),
280            "[TIP] Property-based testing works great with pure functions".to_string(),
281        ]
282    }
283
284    pub fn explain_functional_benefits() -> String {
285        r#"
286## Why Extract Pure Functions?
287
288Pure functions provide several key benefits:
289
2901. **Testability**: Pure functions are trivial to test - just provide input and assert output
2912. **Composability**: Pure functions can be easily combined to create complex behavior
2923. **Reasoning**: No hidden state or side effects makes code easier to understand
2934. **Parallelization**: Pure functions are thread-safe by default
2945. **Debugging**: Predictable behavior makes bugs easier to find and fix
2956. **Reusability**: Pure functions can be used in any context
296
297## Functional Core / Imperative Shell
298
299This architecture pattern separates your application into:
300- **Functional Core**: Pure business logic with no side effects
301- **Imperative Shell**: Thin layer handling I/O and orchestration
302
303This gives you the best of both worlds: testable business logic and necessary I/O operations.
304"#
305        .to_string()
306    }
307}