debtmap/refactoring/guidance/
mod.rs1use 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 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 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 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 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 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}