1#![allow(dead_code)]
4
5use std::collections::HashMap;
8
9#[allow(dead_code)]
11#[derive(Debug, Default)]
12pub struct Bucket {
13 pub name: String,
14 pub entries: HashMap<String, Vec<u8>>,
15 pub write_count: u64,
16}
17
18#[allow(dead_code)]
19impl Bucket {
20 pub fn new(name: &str) -> Self {
21 Self {
22 name: name.to_string(),
23 entries: HashMap::new(),
24 write_count: 0,
25 }
26 }
27
28 pub fn put(&mut self, key: &str, data: Vec<u8>) {
29 self.entries.insert(key.to_string(), data);
30 self.write_count += 1;
31 }
32
33 pub fn get(&self, key: &str) -> Option<&[u8]> {
34 self.entries.get(key).map(|v| v.as_slice())
35 }
36
37 pub fn remove(&mut self, key: &str) -> bool {
38 self.entries.remove(key).is_some()
39 }
40
41 pub fn contains(&self, key: &str) -> bool {
42 self.entries.contains_key(key)
43 }
44
45 pub fn entry_count(&self) -> usize {
46 self.entries.len()
47 }
48
49 pub fn total_bytes(&self) -> usize {
50 self.entries.values().map(|v| v.len()).sum()
51 }
52}
53
54#[allow(dead_code)]
56pub struct StorageBackend {
57 buckets: HashMap<String, Bucket>,
58 read_count: u64,
59}
60
61#[allow(dead_code)]
62impl StorageBackend {
63 pub fn new() -> Self {
64 Self {
65 buckets: HashMap::new(),
66 read_count: 0,
67 }
68 }
69
70 pub fn ensure_bucket(&mut self, name: &str) -> &mut Bucket {
71 self.buckets
72 .entry(name.to_string())
73 .or_insert_with(|| Bucket::new(name))
74 }
75
76 pub fn put(&mut self, bucket: &str, key: &str, data: Vec<u8>) {
77 self.ensure_bucket(bucket).put(key, data);
78 }
79
80 pub fn get(&mut self, bucket: &str, key: &str) -> Option<&[u8]> {
81 self.read_count += 1;
82 self.buckets
83 .get(bucket)?
84 .entries
85 .get(key)
86 .map(|v| v.as_slice())
87 }
88
89 pub fn remove(&mut self, bucket: &str, key: &str) -> bool {
90 self.buckets.get_mut(bucket).is_some_and(|b| b.remove(key))
91 }
92
93 pub fn contains(&self, bucket: &str, key: &str) -> bool {
94 self.buckets.get(bucket).is_some_and(|b| b.contains(key))
95 }
96
97 pub fn bucket_count(&self) -> usize {
98 self.buckets.len()
99 }
100
101 pub fn total_entries(&self) -> usize {
102 self.buckets.values().map(|b| b.entry_count()).sum()
103 }
104
105 pub fn total_bytes(&self) -> usize {
106 self.buckets.values().map(|b| b.total_bytes()).sum()
107 }
108
109 pub fn read_count(&self) -> u64 {
110 self.read_count
111 }
112
113 pub fn drop_bucket(&mut self, name: &str) -> bool {
114 self.buckets.remove(name).is_some()
115 }
116}
117
118impl Default for StorageBackend {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124pub fn new_storage_backend() -> StorageBackend {
125 StorageBackend::new()
126}
127
128pub fn sb_put(sb: &mut StorageBackend, bucket: &str, key: &str, data: Vec<u8>) {
129 sb.put(bucket, key, data);
130}
131
132pub fn sb_get<'a>(sb: &'a mut StorageBackend, bucket: &str, key: &str) -> Option<&'a [u8]> {
133 sb.get(bucket, key)
134}
135
136pub fn sb_remove(sb: &mut StorageBackend, bucket: &str, key: &str) -> bool {
137 sb.remove(bucket, key)
138}
139
140pub fn sb_contains(sb: &StorageBackend, bucket: &str, key: &str) -> bool {
141 sb.contains(bucket, key)
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn empty_on_creation() {
150 let sb = new_storage_backend();
151 assert_eq!(sb.bucket_count(), 0);
152 assert_eq!(sb.total_entries(), 0);
153 }
154
155 #[test]
156 fn put_and_get() {
157 let mut sb = new_storage_backend();
158 sb_put(&mut sb, "assets", "mesh.bin", vec![1, 2, 3]);
159 let data = sb_get(&mut sb, "assets", "mesh.bin").expect("should succeed");
160 assert_eq!(data, &[1, 2, 3]);
161 }
162
163 #[test]
164 fn missing_key_returns_none() {
165 let mut sb = new_storage_backend();
166 assert!(sb_get(&mut sb, "bucket", "missing").is_none());
167 }
168
169 #[test]
170 fn remove_returns_true_once() {
171 let mut sb = new_storage_backend();
172 sb_put(&mut sb, "b", "k", vec![0]);
173 assert!(sb_remove(&mut sb, "b", "k"));
174 assert!(!sb_remove(&mut sb, "b", "k"));
175 }
176
177 #[test]
178 fn contains_check() {
179 let mut sb = new_storage_backend();
180 sb_put(&mut sb, "b", "key", vec![]);
181 assert!(sb_contains(&sb, "b", "key"));
182 assert!(!sb_contains(&sb, "b", "other"));
183 }
184
185 #[test]
186 fn total_bytes_sums_across_buckets() {
187 let mut sb = new_storage_backend();
188 sb_put(&mut sb, "a", "k1", vec![1, 2]);
189 sb_put(&mut sb, "b", "k2", vec![3, 4, 5]);
190 assert_eq!(sb.total_bytes(), 5);
191 }
192
193 #[test]
194 fn drop_bucket() {
195 let mut sb = new_storage_backend();
196 sb_put(&mut sb, "tmp", "k", vec![0]);
197 assert!(sb.drop_bucket("tmp"));
198 assert_eq!(sb.bucket_count(), 0);
199 }
200
201 #[test]
202 fn read_count_increments() {
203 let mut sb = new_storage_backend();
204 sb_put(&mut sb, "b", "k", vec![0]);
205 sb_get(&mut sb, "b", "k");
206 sb_get(&mut sb, "b", "k");
207 assert_eq!(sb.read_count(), 2);
208 }
209
210 #[test]
211 fn bucket_auto_created() {
212 let mut sb = new_storage_backend();
213 sb_put(&mut sb, "new_bucket", "key", vec![7]);
214 assert_eq!(sb.bucket_count(), 1);
215 }
216
217 #[test]
218 fn total_entries_across_buckets() {
219 let mut sb = new_storage_backend();
220 sb_put(&mut sb, "a", "k1", vec![]);
221 sb_put(&mut sb, "a", "k2", vec![]);
222 sb_put(&mut sb, "b", "k3", vec![]);
223 assert_eq!(sb.total_entries(), 3);
224 }
225}