1use std::collections::HashMap;
2
3pub struct CustomMetrics {
5 entries: HashMap<String, serde_json::Value>,
6}
7
8impl CustomMetrics {
9 pub fn new() -> Self {
10 Self {
11 entries: HashMap::new(),
12 }
13 }
14
15 pub fn record(&mut self, key: String, value: serde_json::Value) {
16 self.entries.insert(key, value);
17 }
18
19 pub fn get(&self, key: &str) -> Option<&serde_json::Value> {
20 self.entries.get(key)
21 }
22
23 pub fn to_json(&self) -> serde_json::Value {
24 serde_json::to_value(&self.entries).unwrap_or(serde_json::Value::Null)
25 }
26}
27
28impl Default for CustomMetrics {
29 fn default() -> Self {
30 Self::new()
31 }
32}
33
34#[cfg(test)]
35mod tests {
36 use super::*;
37 use serde_json::json;
38
39 #[test]
40 fn record_and_get() {
41 let mut cm = CustomMetrics::new();
42 cm.record("key".into(), json!(42));
43 assert_eq!(cm.get("key"), Some(&json!(42)));
44 }
45
46 #[test]
47 fn get_missing_returns_none() {
48 let cm = CustomMetrics::new();
49 assert_eq!(cm.get("missing"), None);
50 }
51
52 #[test]
53 fn record_overwrites() {
54 let mut cm = CustomMetrics::new();
55 cm.record("key".into(), json!(1));
56 cm.record("key".into(), json!(2));
57 assert_eq!(cm.get("key"), Some(&json!(2)));
58 }
59
60 #[test]
61 fn to_json_includes_all_entries() {
62 let mut cm = CustomMetrics::new();
63 cm.record("a".into(), json!(1));
64 cm.record("b".into(), json!("two"));
65 let json = cm.to_json();
66 assert_eq!(json.get("a").unwrap(), 1);
67 assert_eq!(json.get("b").unwrap(), "two");
68 }
69}
70
71#[cfg(test)]
72mod proptests {
73 use super::*;
74 use proptest::prelude::*;
75
76 proptest! {
77 #[test]
78 fn record_then_get_consistent(key in "[a-zA-Z_]{1,30}", val in any::<i64>()) {
79 let mut cm = CustomMetrics::new();
80 let json_val = serde_json::json!(val);
81 cm.record(key.clone(), json_val.clone());
82 prop_assert_eq!(cm.get(&key), Some(&json_val));
83 }
84
85 #[test]
86 fn last_write_wins(key in "[a-zA-Z_]{1,30}", v1 in any::<i64>(), v2 in any::<i64>()) {
87 let mut cm = CustomMetrics::new();
88 cm.record(key.clone(), serde_json::json!(v1));
89 cm.record(key.clone(), serde_json::json!(v2));
90 prop_assert_eq!(cm.get(&key), Some(&serde_json::json!(v2)));
91 }
92
93 #[test]
94 fn to_json_contains_all_recorded(
95 entries in proptest::collection::vec(
96 ("[a-z]{1,10}", any::<i64>()),
97 1..20,
98 )
99 ) {
100 let mut cm = CustomMetrics::new();
101 for (k, v) in &entries {
102 cm.record(k.clone(), serde_json::json!(v));
103 }
104 let json = cm.to_json();
105 let mut expected = std::collections::HashMap::new();
107 for (k, v) in &entries {
108 expected.insert(k.clone(), serde_json::json!(v));
109 }
110 for (k, v) in &expected {
111 prop_assert_eq!(json.get(k), Some(v));
112 }
113 }
114 }
115}