oxihuman_core/
access_map.rs1#![allow(dead_code)]
4
5use std::collections::HashMap;
8
9#[allow(dead_code)]
11#[derive(Debug, Clone)]
12pub struct AccessMap {
13 reads: HashMap<String, u64>,
14 writes: HashMap<String, u64>,
15}
16
17#[allow(dead_code)]
18impl AccessMap {
19 pub fn new() -> Self {
20 Self {
21 reads: HashMap::new(),
22 writes: HashMap::new(),
23 }
24 }
25
26 pub fn record_read(&mut self, key: &str) {
27 *self.reads.entry(key.to_string()).or_insert(0) += 1;
28 }
29
30 pub fn record_write(&mut self, key: &str) {
31 *self.writes.entry(key.to_string()).or_insert(0) += 1;
32 }
33
34 pub fn read_count(&self, key: &str) -> u64 {
35 self.reads.get(key).copied().unwrap_or(0)
36 }
37
38 pub fn write_count(&self, key: &str) -> u64 {
39 self.writes.get(key).copied().unwrap_or(0)
40 }
41
42 pub fn total_accesses(&self, key: &str) -> u64 {
43 self.read_count(key) + self.write_count(key)
44 }
45
46 pub fn tracked_key_count(&self) -> usize {
47 let mut keys: std::collections::HashSet<&String> = self.reads.keys().collect();
48 keys.extend(self.writes.keys());
49 keys.len()
50 }
51
52 pub fn most_read(&self) -> Option<(String, u64)> {
53 self.reads
54 .iter()
55 .max_by_key(|(_, &v)| v)
56 .map(|(k, &v)| (k.clone(), v))
57 }
58
59 pub fn most_written(&self) -> Option<(String, u64)> {
60 self.writes
61 .iter()
62 .max_by_key(|(_, &v)| v)
63 .map(|(k, &v)| (k.clone(), v))
64 }
65
66 pub fn clear(&mut self) {
67 self.reads.clear();
68 self.writes.clear();
69 }
70
71 pub fn has_key(&self, key: &str) -> bool {
72 self.reads.contains_key(key) || self.writes.contains_key(key)
73 }
74
75 pub fn all_keys(&self) -> Vec<String> {
76 let mut keys: std::collections::HashSet<String> = self.reads.keys().cloned().collect();
77 keys.extend(self.writes.keys().cloned());
78 let mut v: Vec<String> = keys.into_iter().collect();
79 v.sort();
80 v
81 }
82}
83
84impl Default for AccessMap {
85 fn default() -> Self {
86 Self::new()
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn new_map_is_empty() {
96 let m = AccessMap::new();
97 assert_eq!(m.tracked_key_count(), 0);
98 }
99
100 #[test]
101 fn record_read_increments() {
102 let mut m = AccessMap::new();
103 m.record_read("a");
104 m.record_read("a");
105 assert_eq!(m.read_count("a"), 2);
106 }
107
108 #[test]
109 fn record_write_increments() {
110 let mut m = AccessMap::new();
111 m.record_write("b");
112 assert_eq!(m.write_count("b"), 1);
113 }
114
115 #[test]
116 fn total_accesses_sums_reads_writes() {
117 let mut m = AccessMap::new();
118 m.record_read("x");
119 m.record_write("x");
120 m.record_write("x");
121 assert_eq!(m.total_accesses("x"), 3);
122 }
123
124 #[test]
125 fn missing_key_returns_zero() {
126 let m = AccessMap::new();
127 assert_eq!(m.read_count("missing"), 0);
128 assert_eq!(m.write_count("missing"), 0);
129 }
130
131 #[test]
132 fn most_read_returns_highest() {
133 let mut m = AccessMap::new();
134 m.record_read("a");
135 m.record_read("b");
136 m.record_read("b");
137 let (k, c) = m.most_read().expect("should succeed");
138 assert_eq!(k, "b");
139 assert_eq!(c, 2);
140 }
141
142 #[test]
143 fn clear_resets_all() {
144 let mut m = AccessMap::new();
145 m.record_read("a");
146 m.record_write("b");
147 m.clear();
148 assert_eq!(m.tracked_key_count(), 0);
149 }
150
151 #[test]
152 fn has_key_checks_both() {
153 let mut m = AccessMap::new();
154 m.record_write("w");
155 assert!(m.has_key("w"));
156 assert!(!m.has_key("missing"));
157 }
158
159 #[test]
160 fn all_keys_sorted() {
161 let mut m = AccessMap::new();
162 m.record_read("c");
163 m.record_write("a");
164 m.record_read("b");
165 assert_eq!(
166 m.all_keys(),
167 vec!["a".to_string(), "b".to_string(), "c".to_string()]
168 );
169 }
170
171 #[test]
172 fn default_is_empty() {
173 let m = AccessMap::default();
174 assert_eq!(m.tracked_key_count(), 0);
175 }
176}