Skip to main content

oxihuman_core/
counter_map.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5use std::collections::HashMap;
6
7/// A map that counts occurrences of string keys.
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct CounterMap {
11    counts: HashMap<String, u64>,
12}
13
14#[allow(dead_code)]
15impl CounterMap {
16    pub fn new() -> Self {
17        Self {
18            counts: HashMap::new(),
19        }
20    }
21
22    pub fn increment(&mut self, key: &str) -> u64 {
23        let entry = self.counts.entry(key.to_string()).or_insert(0);
24        *entry += 1;
25        *entry
26    }
27
28    pub fn decrement(&mut self, key: &str) -> u64 {
29        let entry = self.counts.entry(key.to_string()).or_insert(0);
30        *entry = entry.saturating_sub(1);
31        *entry
32    }
33
34    pub fn get(&self, key: &str) -> u64 {
35        self.counts.get(key).copied().unwrap_or(0)
36    }
37
38    pub fn set(&mut self, key: &str, count: u64) {
39        self.counts.insert(key.to_string(), count);
40    }
41
42    pub fn total(&self) -> u64 {
43        self.counts.values().sum()
44    }
45
46    pub fn len(&self) -> usize {
47        self.counts.len()
48    }
49
50    pub fn is_empty(&self) -> bool {
51        self.counts.is_empty()
52    }
53
54    pub fn max_key(&self) -> Option<&str> {
55        self.counts
56            .iter()
57            .max_by_key(|(_, &v)| v)
58            .map(|(k, _)| k.as_str())
59    }
60
61    pub fn min_key(&self) -> Option<&str> {
62        self.counts
63            .iter()
64            .min_by_key(|(_, &v)| v)
65            .map(|(k, _)| k.as_str())
66    }
67
68    pub fn remove(&mut self, key: &str) -> Option<u64> {
69        self.counts.remove(key)
70    }
71
72    pub fn clear(&mut self) {
73        self.counts.clear();
74    }
75
76    pub fn keys(&self) -> Vec<&str> {
77        self.counts.keys().map(|k| k.as_str()).collect()
78    }
79}
80
81impl Default for CounterMap {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_new() {
93        let cm = CounterMap::new();
94        assert!(cm.is_empty());
95    }
96
97    #[test]
98    fn test_increment() {
99        let mut cm = CounterMap::new();
100        assert_eq!(cm.increment("a"), 1);
101        assert_eq!(cm.increment("a"), 2);
102    }
103
104    #[test]
105    fn test_decrement() {
106        let mut cm = CounterMap::new();
107        cm.set("a", 5);
108        assert_eq!(cm.decrement("a"), 4);
109    }
110
111    #[test]
112    fn test_decrement_floor() {
113        let mut cm = CounterMap::new();
114        assert_eq!(cm.decrement("x"), 0);
115    }
116
117    #[test]
118    fn test_get_missing() {
119        let cm = CounterMap::new();
120        assert_eq!(cm.get("nope"), 0);
121    }
122
123    #[test]
124    fn test_total() {
125        let mut cm = CounterMap::new();
126        cm.set("a", 10);
127        cm.set("b", 20);
128        assert_eq!(cm.total(), 30);
129    }
130
131    #[test]
132    fn test_max_key() {
133        let mut cm = CounterMap::new();
134        cm.set("a", 1);
135        cm.set("b", 5);
136        cm.set("c", 3);
137        assert_eq!(cm.max_key(), Some("b"));
138    }
139
140    #[test]
141    fn test_min_key() {
142        let mut cm = CounterMap::new();
143        cm.set("a", 10);
144        cm.set("b", 2);
145        assert_eq!(cm.min_key(), Some("b"));
146    }
147
148    #[test]
149    fn test_remove() {
150        let mut cm = CounterMap::new();
151        cm.set("k", 5);
152        assert_eq!(cm.remove("k"), Some(5));
153        assert!(cm.is_empty());
154    }
155
156    #[test]
157    fn test_clear() {
158        let mut cm = CounterMap::new();
159        cm.increment("a");
160        cm.increment("b");
161        cm.clear();
162        assert!(cm.is_empty());
163    }
164}