Skip to main content

tldr_core/patterns/
format.rs

1//! Text formatting for pattern reports
2//!
3//! Provides human-readable text output for pattern analysis.
4
5use crate::types::{NamingConvention, PatternReport};
6
7/// Format a pattern report as human-readable text
8pub fn format_pattern_report_text(report: &PatternReport) -> String {
9    let mut output = String::new();
10
11    // Header
12    output.push_str("# Pattern Analysis Report\n\n");
13
14    // Metadata
15    output.push_str("## Summary\n");
16    output.push_str(&format!(
17        "- Files analyzed: {}\n",
18        report.metadata.files_analyzed
19    ));
20    output.push_str(&format!(
21        "- Files skipped: {}\n",
22        report.metadata.files_skipped
23    ));
24    output.push_str(&format!("- Duration: {}ms\n", report.metadata.duration_ms));
25    output.push_str(&format!(
26        "- Patterns found: {}\n\n",
27        report.metadata.patterns_after_filter
28    ));
29
30    // Languages
31    if !report
32        .metadata
33        .language_distribution
34        .files_by_language
35        .is_empty()
36    {
37        output.push_str("### Languages\n");
38        for (lang, count) in &report.metadata.language_distribution.files_by_language {
39            output.push_str(&format!("- {}: {} files\n", lang, count));
40        }
41        output.push('\n');
42    }
43
44    // Pattern sections
45    if let Some(ref pattern) = report.soft_delete {
46        output.push_str("## Soft Delete Pattern\n");
47        output.push_str(&format!("- Detected: {}\n", pattern.detected));
48        output.push_str(&format!(
49            "- Confidence: {:.0}%\n",
50            pattern.confidence * 100.0
51        ));
52        if !pattern.column_names.is_empty() {
53            output.push_str(&format!("- Columns: {}\n", pattern.column_names.join(", ")));
54        }
55        output.push('\n');
56    }
57
58    if let Some(ref pattern) = report.error_handling {
59        output.push_str("## Error Handling\n");
60        output.push_str(&format!(
61            "- Confidence: {:.0}%\n",
62            pattern.confidence * 100.0
63        ));
64        if !pattern.patterns.is_empty() {
65            output.push_str(&format!("- Patterns: {}\n", pattern.patterns.join(", ")));
66        }
67        if !pattern.exception_types.is_empty() {
68            output.push_str(&format!(
69                "- Exception types: {}\n",
70                pattern.exception_types.join(", ")
71            ));
72        }
73        output.push('\n');
74    }
75
76    if let Some(ref pattern) = report.naming {
77        output.push_str("## Naming Conventions\n");
78        output.push_str(&format!(
79            "- Consistency: {:.0}%\n",
80            pattern.consistency_score * 100.0
81        ));
82        output.push_str(&format!(
83            "- Functions: {}\n",
84            convention_name(&pattern.functions)
85        ));
86        output.push_str(&format!(
87            "- Classes: {}\n",
88            convention_name(&pattern.classes)
89        ));
90        output.push_str(&format!(
91            "- Constants: {}\n",
92            convention_name(&pattern.constants)
93        ));
94        if let Some(ref prefix) = pattern.private_prefix {
95            output.push_str(&format!("- Private prefix: '{}'\n", prefix));
96        }
97        if !pattern.violations.is_empty() {
98            output.push_str(&format!("- Violations: {}\n", pattern.violations.len()));
99        }
100        output.push('\n');
101    }
102
103    if let Some(ref pattern) = report.resource_management {
104        output.push_str("## Resource Management\n");
105        output.push_str(&format!(
106            "- Confidence: {:.0}%\n",
107            pattern.confidence * 100.0
108        ));
109        if !pattern.patterns.is_empty() {
110            output.push_str(&format!("- Patterns: {}\n", pattern.patterns.join(", ")));
111        }
112        output.push('\n');
113    }
114
115    if let Some(ref pattern) = report.validation {
116        output.push_str("## Input Validation\n");
117        output.push_str(&format!(
118            "- Confidence: {:.0}%\n",
119            pattern.confidence * 100.0
120        ));
121        if !pattern.frameworks.is_empty() {
122            output.push_str(&format!(
123                "- Frameworks: {}\n",
124                pattern.frameworks.join(", ")
125            ));
126        }
127        if !pattern.patterns.is_empty() {
128            output.push_str(&format!("- Patterns: {}\n", pattern.patterns.join(", ")));
129        }
130        output.push('\n');
131    }
132
133    if let Some(ref pattern) = report.test_idioms {
134        output.push_str("## Test Idioms\n");
135        output.push_str(&format!(
136            "- Confidence: {:.0}%\n",
137            pattern.confidence * 100.0
138        ));
139        if let Some(ref framework) = pattern.framework {
140            output.push_str(&format!("- Framework: {}\n", framework));
141        }
142        if !pattern.patterns.is_empty() {
143            output.push_str(&format!("- Patterns: {}\n", pattern.patterns.join(", ")));
144        }
145        output.push_str(&format!("- Fixture usage: {}\n", pattern.fixture_usage));
146        output.push_str(&format!("- Mock usage: {}\n", pattern.mock_usage));
147        output.push('\n');
148    }
149
150    if let Some(ref pattern) = report.import_patterns {
151        output.push_str("## Import Patterns\n");
152        output.push_str(&format!("- Grouping: {:?}\n", pattern.grouping_style));
153        output.push_str(&format!("- Style: {:?}\n", pattern.absolute_vs_relative));
154        output.push_str(&format!("- Star imports: {:?}\n", pattern.star_imports));
155        if !pattern.alias_conventions.is_empty() {
156            output.push_str("- Aliases:\n");
157            for alias in &pattern.alias_conventions {
158                output.push_str(&format!("  - {} -> {}\n", alias.module, alias.alias));
159            }
160        }
161        output.push('\n');
162    }
163
164    if let Some(ref pattern) = report.type_coverage {
165        output.push_str("## Type Coverage\n");
166        output.push_str(&format!(
167            "- Overall: {:.0}%\n",
168            pattern.coverage_overall * 100.0
169        ));
170        output.push_str(&format!(
171            "- Functions: {:.0}%\n",
172            pattern.coverage_functions * 100.0
173        ));
174        output.push_str(&format!(
175            "- Variables: {:.0}%\n",
176            pattern.coverage_variables * 100.0
177        ));
178        output.push_str(&format!("- TypeVar usage: {}\n", pattern.typevar_usage));
179        if !pattern.generic_patterns.is_empty() {
180            output.push_str(&format!(
181                "- Generic patterns: {}\n",
182                pattern.generic_patterns.join(", ")
183            ));
184        }
185        output.push('\n');
186    }
187
188    if let Some(ref pattern) = report.api_conventions {
189        output.push_str("## API Conventions\n");
190        output.push_str(&format!(
191            "- Confidence: {:.0}%\n",
192            pattern.confidence * 100.0
193        ));
194        if let Some(ref framework) = pattern.framework {
195            output.push_str(&format!("- Framework: {}\n", framework));
196        }
197        if !pattern.patterns.is_empty() {
198            output.push_str(&format!("- Patterns: {}\n", pattern.patterns.join(", ")));
199        }
200        if let Some(ref orm) = pattern.orm_usage {
201            output.push_str(&format!("- ORM: {}\n", orm));
202        }
203        output.push('\n');
204    }
205
206    if let Some(ref pattern) = report.async_patterns {
207        output.push_str("## Async/Concurrency\n");
208        output.push_str(&format!(
209            "- Confidence: {:.0}%\n",
210            pattern.concurrency_confidence * 100.0
211        ));
212        if !pattern.patterns.is_empty() {
213            output.push_str(&format!("- Patterns: {}\n", pattern.patterns.join(", ")));
214        }
215        if !pattern.sync_primitives.is_empty() {
216            output.push_str(&format!(
217                "- Sync primitives: {}\n",
218                pattern.sync_primitives.join(", ")
219            ));
220        }
221        output.push('\n');
222    }
223
224    // Constraints
225    if !report.constraints.is_empty() {
226        output.push_str("## LLM Constraints\n");
227        for constraint in &report.constraints {
228            output.push_str(&format!(
229                "- [{}] {}\n",
230                constraint.category, constraint.rule
231            ));
232        }
233        output.push('\n');
234    }
235
236    // Conflicts
237    if !report.conflicts.is_empty() {
238        output.push_str("## Conflicts Detected\n");
239        for conflict in &report.conflicts {
240            output.push_str(&format!("- {}\n", conflict));
241        }
242        output.push('\n');
243    }
244
245    output
246}
247
248fn convention_name(conv: &NamingConvention) -> &'static str {
249    match conv {
250        NamingConvention::SnakeCase => "snake_case",
251        NamingConvention::CamelCase => "camelCase",
252        NamingConvention::PascalCase => "PascalCase",
253        NamingConvention::UpperSnakeCase => "UPPER_SNAKE_CASE",
254        NamingConvention::Mixed => "Mixed",
255    }
256}