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}