This commit is contained in:
169
src/export/exporter.rs
Normal file
169
src/export/exporter.rs
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::models::{
|
||||||
|
AnalysisSummary, ByLanguage, ByPriority, ComplexityDistribution, Priority, TechDebtItem,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Exporter;
|
||||||
|
|
||||||
|
impl Exporter {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn export_json(&self, items: &[TechDebtItem], output: &PathBuf) -> anyhow::Result<()> {
|
||||||
|
let summary = self.create_summary(items);
|
||||||
|
|
||||||
|
let export_data = ExportData {
|
||||||
|
summary,
|
||||||
|
items: items.to_vec(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let json = serde_json::to_string_pretty(&export_data)?;
|
||||||
|
let mut file = File::create(output)?;
|
||||||
|
file.write_all(json.as_bytes())?;
|
||||||
|
|
||||||
|
println!("Exported {} items to {}", items.len(), output.display());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn export_markdown(&self, items: &[TechDebtItem], output: &PathBuf) -> anyhow::Result<()> {
|
||||||
|
let mut content = String::new();
|
||||||
|
|
||||||
|
content.push_str("# Technical Debt Report\n\n");
|
||||||
|
content.push_str(&format!(
|
||||||
|
"**Generated:** {}\n\n",
|
||||||
|
chrono::Local::now().to_rfc2829()
|
||||||
|
));
|
||||||
|
|
||||||
|
let summary = self.create_summary(items);
|
||||||
|
content.push_str("## Summary\n\n");
|
||||||
|
content.push_str("| Metric | Count |\n|--------|-------|\n");
|
||||||
|
content.push_str(&format!("| Total Items | {} |\n", summary.total_items));
|
||||||
|
content.push_str(&format!("| Critical | {} |\n", summary.by_priority.critical));
|
||||||
|
content.push_str(&format!("| High | {} |\n", summary.by_priority.high));
|
||||||
|
content.push_str(&format!("| Medium | {} |\n", summary.by_priority.medium));
|
||||||
|
content.push_str(&format!("| Low | {} |\n", summary.by_priority.low));
|
||||||
|
content.push_str("\n");
|
||||||
|
|
||||||
|
content.push_str("## By Priority\n\n");
|
||||||
|
content.push_str("| Priority | Count | Bar |\n|---------|-------|-----|\n");
|
||||||
|
for (priority_str, count) in [
|
||||||
|
("Critical", summary.by_priority.critical),
|
||||||
|
("High", summary.by_priority.high),
|
||||||
|
("Medium", summary.by_priority.medium),
|
||||||
|
("Low", summary.by_priority.low),
|
||||||
|
] {
|
||||||
|
let bar = "█".repeat(count.min(50));
|
||||||
|
content.push_str(&format!("| {} | {} | {} |\n", priority_str, count, bar));
|
||||||
|
}
|
||||||
|
content.push_str("\n");
|
||||||
|
|
||||||
|
content.push_str("## By Language\n\n");
|
||||||
|
content.push_str("| Language | Count |\n|----------|-------|\n");
|
||||||
|
for lang in &summary.by_language.items {
|
||||||
|
content.push_str(&format!("| {} | {} |\n", lang.language, lang.count));
|
||||||
|
}
|
||||||
|
content.push_str("\n");
|
||||||
|
|
||||||
|
content.push_str("## Technical Debt Items\n\n");
|
||||||
|
|
||||||
|
let priority_order = ["Critical", "High", "Medium", "Low"];
|
||||||
|
for priority_str in priority_order {
|
||||||
|
let priority_items: Vec<_> = items
|
||||||
|
.iter()
|
||||||
|
.filter(|i| i.priority.as_str() == priority_str)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !priority_items.is_empty() {
|
||||||
|
content.push_str(&format!("### {}\n\n", priority_str));
|
||||||
|
|
||||||
|
let mut sorted_items: Vec<_> = priority_items.iter().collect();
|
||||||
|
sorted_items.sort_by_key(|i| &i.location.path);
|
||||||
|
|
||||||
|
for item in sorted_items {
|
||||||
|
content.push_str(&format!(
|
||||||
|
"- **{}** at `{}:{}`\n",
|
||||||
|
item.keyword,
|
||||||
|
item.location.path.display(),
|
||||||
|
item.location.line
|
||||||
|
));
|
||||||
|
content.push_str(&format!(" - {}\n", self.truncate(&item.content, 100)));
|
||||||
|
content.push_str(&format!(" - Complexity: {}/10\n", item.complexity_score));
|
||||||
|
content.push_str(&format!(" - Language: {}\n", item.metadata.language));
|
||||||
|
content.push_str("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = File::create(output)?;
|
||||||
|
file.write_all(content.as_bytes())?;
|
||||||
|
|
||||||
|
println!("Exported to {}", output.display());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_summary(&self, items: &[TechDebtItem]) {
|
||||||
|
let summary = self.create_summary(items);
|
||||||
|
|
||||||
|
println!();
|
||||||
|
println!("═══════════════════════════════════════════");
|
||||||
|
println!(" TECHNICAL DEBT ANALYSIS ");
|
||||||
|
println!("═══════════════════════════════════════════");
|
||||||
|
println!();
|
||||||
|
println!(" Total Items: {}", summary.total_items);
|
||||||
|
println!();
|
||||||
|
println!(" Priority Breakdown:");
|
||||||
|
println!(" Critical: {}", summary.by_priority.critical);
|
||||||
|
println!(" High: {}", summary.by_priority.high);
|
||||||
|
println!(" Medium: {}", summary.by_priority.medium);
|
||||||
|
println!(" Low: {}", summary.by_priority.low);
|
||||||
|
println!();
|
||||||
|
println!(" Complexity Distribution:");
|
||||||
|
println!(" Low (1-3): {}", summary.complexity_distribution.low);
|
||||||
|
println!(" Medium (4-6): {}", summary.complexity_distribution.medium);
|
||||||
|
println!(" High (7-8): {}", summary.complexity_distribution.high);
|
||||||
|
println!(" Critical (9+): {}", summary.complexity_distribution.critical);
|
||||||
|
println!();
|
||||||
|
println!(" By Language:");
|
||||||
|
for lang in summary.by_language.items.iter().take(5) {
|
||||||
|
println!(" {}: {}", lang.language, lang.count);
|
||||||
|
}
|
||||||
|
if summary.by_language.items.len() > 5 {
|
||||||
|
println!(" ... and {} more", summary.by_language.items.len() - 5);
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
println!("═══════════════════════════════════════════");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_summary(&self, items: &[TechDebtItem]) -> AnalysisSummary {
|
||||||
|
let by_priority = ByPriority::from_items(items);
|
||||||
|
let by_language = ByLanguage::from_items(items);
|
||||||
|
let complexity_distribution = ComplexityDistribution::from_items(items);
|
||||||
|
|
||||||
|
AnalysisSummary {
|
||||||
|
total_items: items.len(),
|
||||||
|
by_priority,
|
||||||
|
by_language,
|
||||||
|
complexity_distribution,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn truncate(&self, s: &str, max_len: usize) -> String {
|
||||||
|
if s.len() <= max_len {
|
||||||
|
s.to_string()
|
||||||
|
} else {
|
||||||
|
let mut truncated = s[..max_len - 3].to_string();
|
||||||
|
truncated.push_str("...");
|
||||||
|
truncated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
struct ExportData {
|
||||||
|
summary: AnalysisSummary,
|
||||||
|
items: Vec<TechDebtItem>,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user