1use collections::HashMap;
  2use serde::{Deserialize, Serialize};
  3use std::{fmt::Display, sync::Arc};
  4
  5#[derive(Debug, Default, Clone, Serialize, Deserialize)]
  6pub struct ToolMetrics {
  7    pub use_counts: HashMap<Arc<str>, u32>,
  8    pub failure_counts: HashMap<Arc<str>, u32>,
  9}
 10
 11impl ToolMetrics {
 12    pub fn insert(&mut self, tool_name: Arc<str>, succeeded: bool) {
 13        *self.use_counts.entry(tool_name.clone()).or_insert(0) += 1;
 14        if !succeeded {
 15            *self.failure_counts.entry(tool_name).or_insert(0) += 1;
 16        }
 17    }
 18
 19    pub fn merge(&mut self, other: &ToolMetrics) {
 20        for (tool_name, use_count) in &other.use_counts {
 21            *self.use_counts.entry(tool_name.clone()).or_insert(0) += use_count;
 22        }
 23        for (tool_name, failure_count) in &other.failure_counts {
 24            *self.failure_counts.entry(tool_name.clone()).or_insert(0) += failure_count;
 25        }
 26    }
 27
 28    pub fn is_empty(&self) -> bool {
 29        self.use_counts.is_empty() && self.failure_counts.is_empty()
 30    }
 31}
 32
 33impl Display for ToolMetrics {
 34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 35        let mut failure_rates: Vec<(Arc<str>, f64)> = Vec::new();
 36
 37        for (tool_name, use_count) in &self.use_counts {
 38            let failure_count = self.failure_counts.get(tool_name).cloned().unwrap_or(0);
 39            if *use_count > 0 {
 40                let failure_rate = failure_count as f64 / *use_count as f64;
 41                failure_rates.push((tool_name.clone(), failure_rate));
 42            }
 43        }
 44
 45        // Sort by failure rate descending
 46        failure_rates.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
 47
 48        // Table dimensions
 49        let tool_width = 30;
 50        let count_width = 10;
 51        let rate_width = 10;
 52
 53        // Write table top border
 54        writeln!(
 55            f,
 56            "┌{}┬{}┬{}┬{}┐",
 57            "─".repeat(tool_width),
 58            "─".repeat(count_width),
 59            "─".repeat(count_width),
 60            "─".repeat(rate_width)
 61        )?;
 62
 63        // Write header row
 64        writeln!(
 65            f,
 66            "│{:^30}│{:^10}│{:^10}│{:^10}│",
 67            "Tool", "Uses", "Failures", "Rate"
 68        )?;
 69
 70        // Write header-data separator
 71        writeln!(
 72            f,
 73            "├{}┼{}┼{}┼{}┤",
 74            "─".repeat(tool_width),
 75            "─".repeat(count_width),
 76            "─".repeat(count_width),
 77            "─".repeat(rate_width)
 78        )?;
 79
 80        // Write data rows
 81        for (tool_name, failure_rate) in failure_rates {
 82            let use_count = self.use_counts.get(&tool_name).cloned().unwrap_or(0);
 83            let failure_count = self.failure_counts.get(&tool_name).cloned().unwrap_or(0);
 84            writeln!(
 85                f,
 86                "│{:<30}│{:^10}│{:^10}│{:^10}│",
 87                tool_name,
 88                use_count,
 89                failure_count,
 90                format!("{}%", (failure_rate * 100.0).round())
 91            )?;
 92        }
 93
 94        // Write table bottom border
 95        writeln!(
 96            f,
 97            "└{}┴{}┴{}┴{}┘",
 98            "─".repeat(tool_width),
 99            "─".repeat(count_width),
100            "─".repeat(count_width),
101            "─".repeat(rate_width)
102        )?;
103
104        Ok(())
105    }
106}