tool_metrics.rs

  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
 29impl Display for ToolMetrics {
 30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 31        let mut failure_rates: Vec<(Arc<str>, f64)> = Vec::new();
 32
 33        for (tool_name, use_count) in &self.use_counts {
 34            let failure_count = self.failure_counts.get(tool_name).cloned().unwrap_or(0);
 35            if *use_count > 0 {
 36                let failure_rate = failure_count as f64 / *use_count as f64;
 37                failure_rates.push((tool_name.clone(), failure_rate));
 38            }
 39        }
 40
 41        // Sort by failure rate descending
 42        failure_rates.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
 43
 44        // Table dimensions
 45        let tool_width = 30;
 46        let count_width = 10;
 47        let rate_width = 10;
 48
 49        // Write table top border
 50        writeln!(
 51            f,
 52            "┌{}┬{}┬{}┬{}┐",
 53            "".repeat(tool_width),
 54            "".repeat(count_width),
 55            "".repeat(count_width),
 56            "".repeat(rate_width)
 57        )?;
 58
 59        // Write header row
 60        writeln!(
 61            f,
 62            "│{:^30}│{:^10}│{:^10}│{:^10}│",
 63            "Tool", "Uses", "Failures", "Rate"
 64        )?;
 65
 66        // Write header-data separator
 67        writeln!(
 68            f,
 69            "├{}┼{}┼{}┼{}┤",
 70            "".repeat(tool_width),
 71            "".repeat(count_width),
 72            "".repeat(count_width),
 73            "".repeat(rate_width)
 74        )?;
 75
 76        // Write data rows
 77        for (tool_name, failure_rate) in failure_rates {
 78            let use_count = self.use_counts.get(&tool_name).cloned().unwrap_or(0);
 79            let failure_count = self.failure_counts.get(&tool_name).cloned().unwrap_or(0);
 80            writeln!(
 81                f,
 82                "│{:^30}│{:^10}│{:^10}│{:^10}│",
 83                tool_name,
 84                use_count,
 85                failure_count,
 86                format!("{}%", (failure_rate * 100.0).round())
 87            )?;
 88        }
 89
 90        // Write table bottom border
 91        writeln!(
 92            f,
 93            "└{}┴{}┴{}┴{}┘",
 94            "".repeat(tool_width),
 95            "".repeat(count_width),
 96            "".repeat(count_width),
 97            "".repeat(rate_width)
 98        )?;
 99
100        Ok(())
101    }
102}