vault_audit_tools/commands/
kv_summary.rs1use crate::utils::format::format_number;
45use anyhow::{Context, Result};
46use std::fs::File;
47use std::io::BufReader;
48
49pub fn run(csv_file: &str) -> Result<()> {
50 let file = File::open(csv_file).context("Failed to open CSV file")?;
51 let reader = BufReader::new(file);
52 let mut csv_reader = csv::Reader::from_reader(reader);
53
54 let mut rows = Vec::new();
55 for result in csv_reader.records() {
56 let record = result?;
57 rows.push(record);
58 }
59
60 if rows.is_empty() {
61 println!("No data found in {}", csv_file);
62 return Ok(());
63 }
64
65 println!("\n{}", "=".repeat(70));
66 println!("{:^70}", "KV Usage Summary Report");
67 println!("{:^70}", format!("Source: {}", csv_file));
68 println!("{}\n", "=".repeat(70));
69
70 let total_paths = rows.len();
72 let mut total_clients = 0;
73 let mut total_operations = 0;
74
75 let headers = csv_reader.headers()?.clone();
77 let unique_clients_idx = headers.iter().position(|h| h == "unique_clients");
78 let operations_idx = headers.iter().position(|h| h == "operations_count");
79
80 for row in &rows {
81 if let Some(idx) = unique_clients_idx {
82 if let Ok(n) = row.get(idx).unwrap_or("0").parse::<usize>() {
83 total_clients += n;
84 }
85 }
86 if let Some(idx) = operations_idx {
87 if let Ok(n) = row.get(idx).unwrap_or("0").parse::<usize>() {
88 total_operations += n;
89 }
90 }
91 }
92
93 println!("Overview:");
94 println!(" • Total KV Paths: {}", total_paths);
95 println!(
96 " • Total Unique Clients: {}",
97 format_number(total_clients)
98 );
99 println!(" • Total Operations: {}", format_number(total_operations));
100 println!("\n{}\n", "-".repeat(70));
101
102 let kv_path_idx = headers.iter().position(|h| h == "kv_path");
104 let entity_ids_idx = headers.iter().position(|h| h == "entity_ids");
105 let alias_names_idx = headers.iter().position(|h| h == "alias_names");
106 let sample_paths_idx = headers.iter().position(|h| h == "sample_paths_accessed");
107
108 for (i, row) in rows.iter().enumerate() {
109 println!(
110 "{}. KV Path: {}",
111 i + 1,
112 kv_path_idx.and_then(|idx| row.get(idx)).unwrap_or("N/A")
113 );
114
115 if let Some(idx) = unique_clients_idx {
116 println!(" Unique Clients: {}", row.get(idx).unwrap_or("0"));
117 }
118
119 if let Some(idx) = operations_idx {
120 println!(" Total Operations: {}", row.get(idx).unwrap_or("0"));
121 }
122
123 if let Some(idx) = entity_ids_idx {
124 println!(" Entity IDs: {}", row.get(idx).unwrap_or("N/A"));
125 }
126
127 if let Some(idx) = alias_names_idx {
128 if let Some(names) = row.get(idx) {
129 if !names.is_empty() {
130 println!(" Alias Names: {}", names);
131 }
132 }
133 }
134
135 if let Some(idx) = sample_paths_idx {
136 if let Some(paths) = row.get(idx) {
137 let display_paths = if paths.len() > 80 {
138 format!("{}...", &paths[..77])
139 } else {
140 paths.to_string()
141 };
142 println!(" Sample Paths: {}", display_paths);
143 }
144 }
145
146 println!();
147 }
148
149 println!("{}", "-".repeat(70));
150 println!("Report complete. Analyzed {} KV paths.\n", total_paths);
151
152 Ok(())
153}