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}