use std::collections::{BTreeMap, HashSet}; use common_contracts::dtos::TimeSeriesFinancialDto; use chrono::Datelike; /// Formats a list of TimeSeriesFinancialDto into a Markdown table. /// The table columns are years (sorted descending), and rows are metrics. pub fn format_financials_to_markdown(financials: &[TimeSeriesFinancialDto]) -> String { if financials.is_empty() { return "No financial data available.".to_string(); } // 1. Group by Year and Metric // Map> let mut data_map: BTreeMap> = BTreeMap::new(); let mut years_set: HashSet = HashSet::new(); for item in financials { let year = item.period_date.year(); years_set.insert(year); data_map .entry(item.metric_name.clone()) .or_default() .insert(year, item.value); } // 2. Sort years descending (recent first) let mut sorted_years: Vec = years_set.into_iter().collect(); sorted_years.sort_by(|a, b| b.cmp(a)); // Limit to recent 5 years to keep table readable let display_years = if sorted_years.len() > 5 { &sorted_years[..5] } else { &sorted_years }; // 3. Build Markdown Table let mut markdown = String::new(); // Header markdown.push_str("| Metric |"); for year in display_years { markdown.push_str(&format!(" {} |", year)); } markdown.push('\n'); // Separator markdown.push_str("| :--- |"); for _ in display_years { markdown.push_str(" ---: |"); } markdown.push('\n'); // Rows for (metric, year_values) in data_map { markdown.push_str(&format!("| {} |", metric)); for year in display_years { if let Some(value) = year_values.get(year) { // Format large numbers or percentages intelligently if needed. // For now, simple float formatting. markdown.push_str(&format!(" {:.2} |", value)); } else { markdown.push_str(" - |"); } } markdown.push('\n'); } markdown }