This commit is contained in:
181
app/techdebt-tracker-cli/src/export/exporter.rs
Normal file
181
app/techdebt-tracker-cli/src/export/exporter.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::models::{
|
||||
AnalysisSummary, ByLanguage, ByPriority, ComplexityDistribution, 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