1use crate::document::HiveDocument;
46use crate::error::Result;
47
48#[cfg(feature = "std")]
49use std::sync::{Arc, RwLock};
50
51pub trait DocumentStore: Send + Sync {
63 fn save(&mut self, doc: &HiveDocument) -> Result<()>;
69
70 fn load(&self) -> Result<Option<HiveDocument>>;
75
76 fn clear(&mut self) -> Result<()>;
80
81 fn has_document(&self) -> bool {
83 self.load().ok().flatten().is_some()
84 }
85}
86
87#[cfg(feature = "std")]
92#[derive(Default)]
93pub struct MemoryStore {
94 document: RwLock<Option<HiveDocument>>,
95}
96
97#[cfg(feature = "std")]
98impl MemoryStore {
99 pub fn new() -> Self {
101 Self::default()
102 }
103
104 pub fn with_document(doc: HiveDocument) -> Self {
106 Self {
107 document: RwLock::new(Some(doc)),
108 }
109 }
110}
111
112#[cfg(feature = "std")]
113impl DocumentStore for MemoryStore {
114 fn save(&mut self, doc: &HiveDocument) -> Result<()> {
115 let mut stored = self.document.write().unwrap();
116 *stored = Some(doc.clone());
117 Ok(())
118 }
119
120 fn load(&self) -> Result<Option<HiveDocument>> {
121 let stored = self.document.read().unwrap();
122 Ok(stored.clone())
123 }
124
125 fn clear(&mut self) -> Result<()> {
126 let mut stored = self.document.write().unwrap();
127 *stored = None;
128 Ok(())
129 }
130}
131
132#[cfg(feature = "std")]
137pub struct FileStore {
138 path: std::path::PathBuf,
139}
140
141#[cfg(feature = "std")]
142impl FileStore {
143 pub fn new<P: Into<std::path::PathBuf>>(path: P) -> Self {
145 Self { path: path.into() }
146 }
147
148 pub fn path(&self) -> &std::path::Path {
150 &self.path
151 }
152}
153
154#[cfg(feature = "std")]
155impl DocumentStore for FileStore {
156 fn save(&mut self, doc: &HiveDocument) -> Result<()> {
157 let data = doc.encode();
158 std::fs::write(&self.path, data).map_err(|e| {
159 crate::error::BleError::NotSupported(format!("Failed to write document: {}", e))
160 })?;
161 Ok(())
162 }
163
164 fn load(&self) -> Result<Option<HiveDocument>> {
165 match std::fs::read(&self.path) {
166 Ok(data) => Ok(HiveDocument::decode(&data)),
167 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
168 Err(e) => Err(crate::error::BleError::NotSupported(format!(
169 "Failed to read document: {}",
170 e
171 ))),
172 }
173 }
174
175 fn clear(&mut self) -> Result<()> {
176 match std::fs::remove_file(&self.path) {
177 Ok(()) => Ok(()),
178 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
179 Err(e) => Err(crate::error::BleError::NotSupported(format!(
180 "Failed to clear document: {}",
181 e
182 ))),
183 }
184 }
185}
186
187#[cfg(feature = "std")]
189pub struct SharedStore<S: DocumentStore> {
190 inner: Arc<RwLock<S>>,
191}
192
193#[cfg(feature = "std")]
194impl<S: DocumentStore> SharedStore<S> {
195 pub fn new(store: S) -> Self {
197 Self {
198 inner: Arc::new(RwLock::new(store)),
199 }
200 }
201}
202
203#[cfg(feature = "std")]
204impl<S: DocumentStore> Clone for SharedStore<S> {
205 fn clone(&self) -> Self {
206 Self {
207 inner: self.inner.clone(),
208 }
209 }
210}
211
212#[cfg(feature = "std")]
213impl<S: DocumentStore> DocumentStore for SharedStore<S> {
214 fn save(&mut self, doc: &HiveDocument) -> Result<()> {
215 let mut inner = self.inner.write().unwrap();
216 inner.save(doc)
217 }
218
219 fn load(&self) -> Result<Option<HiveDocument>> {
220 let inner = self.inner.read().unwrap();
221 inner.load()
222 }
223
224 fn clear(&mut self) -> Result<()> {
225 let mut inner = self.inner.write().unwrap();
226 inner.clear()
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233 use crate::NodeId;
234
235 #[test]
236 fn test_memory_store() {
237 let mut store = MemoryStore::new();
238
239 assert!(store.load().unwrap().is_none());
241 assert!(!store.has_document());
242
243 let doc = HiveDocument::new(NodeId::new(0x12345678));
245 store.save(&doc).unwrap();
246
247 let loaded = store.load().unwrap().unwrap();
249 assert_eq!(loaded.node_id.as_u32(), 0x12345678);
250 assert!(store.has_document());
251
252 store.clear().unwrap();
254 assert!(store.load().unwrap().is_none());
255 }
256
257 #[test]
258 fn test_file_store() {
259 let temp_dir = std::env::temp_dir();
260 let path = temp_dir.join("hive_test_doc.bin");
261
262 let _ = std::fs::remove_file(&path);
264
265 let mut store = FileStore::new(&path);
266
267 assert!(store.load().unwrap().is_none());
269
270 let mut doc = HiveDocument::new(NodeId::new(0xAABBCCDD));
272 doc.increment_counter();
273 store.save(&doc).unwrap();
274
275 let loaded = store.load().unwrap().unwrap();
277 assert_eq!(loaded.node_id.as_u32(), 0xAABBCCDD);
278 assert_eq!(loaded.counter.value(), 1);
279
280 store.clear().unwrap();
282 assert!(store.load().unwrap().is_none());
283 }
284
285 #[test]
286 fn test_shared_store() {
287 let store = MemoryStore::new();
288 let mut shared = SharedStore::new(store);
289
290 let doc = HiveDocument::new(NodeId::new(0x11111111));
291 shared.save(&doc).unwrap();
292
293 let shared2 = shared.clone();
295 let loaded = shared2.load().unwrap().unwrap();
296 assert_eq!(loaded.node_id.as_u32(), 0x11111111);
297 }
298}