From a2cf937f888f69cda5da47470b5b6abe6dfaab5e Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Thu, 5 Feb 2026 14:41:03 +0000 Subject: [PATCH] Initial upload with CI/CD workflow --- src/export/exporter.rs | 169 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 src/export/exporter.rs diff --git a/src/export/exporter.rs b/src/export/exporter.rs new file mode 100644 index 0000000..eeebc7b --- /dev/null +++ b/src/export/exporter.rs @@ -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, +}