1use crate::document::HiveDocument;
31use crate::error::Result;
32
33#[cfg(feature = "std")]
34use std::sync::{Arc, RwLock};
35
36pub trait DocumentStore: Send + Sync {
48 fn save(&mut self, doc: &HiveDocument) -> Result<()>;
54
55 fn load(&self) -> Result<Option<HiveDocument>>;
60
61 fn clear(&mut self) -> Result<()>;
65
66 fn has_document(&self) -> bool {
68 self.load().ok().flatten().is_some()
69 }
70}
71
72#[cfg(feature = "std")]
77#[derive(Default)]
78pub struct MemoryStore {
79 document: RwLock<Option<HiveDocument>>,
80}
81
82#[cfg(feature = "std")]
83impl MemoryStore {
84 pub fn new() -> Self {
86 Self::default()
87 }
88
89 pub fn with_document(doc: HiveDocument) -> Self {
91 Self {
92 document: RwLock::new(Some(doc)),
93 }
94 }
95}
96
97#[cfg(feature = "std")]
98impl DocumentStore for MemoryStore {
99 fn save(&mut self, doc: &HiveDocument) -> Result<()> {
100 let mut stored = self.document.write().unwrap();
101 *stored = Some(doc.clone());
102 Ok(())
103 }
104
105 fn load(&self) -> Result<Option<HiveDocument>> {
106 let stored = self.document.read().unwrap();
107 Ok(stored.clone())
108 }
109
110 fn clear(&mut self) -> Result<()> {
111 let mut stored = self.document.write().unwrap();
112 *stored = None;
113 Ok(())
114 }
115}
116
117#[cfg(feature = "std")]
122pub struct FileStore {
123 path: std::path::PathBuf,
124}
125
126#[cfg(feature = "std")]
127impl FileStore {
128 pub fn new<P: Into<std::path::PathBuf>>(path: P) -> Self {
130 Self { path: path.into() }
131 }
132
133 pub fn path(&self) -> &std::path::Path {
135 &self.path
136 }
137}
138
139#[cfg(feature = "std")]
140impl DocumentStore for FileStore {
141 fn save(&mut self, doc: &HiveDocument) -> Result<()> {
142 let data = doc.encode();
143 std::fs::write(&self.path, data).map_err(|e| {
144 crate::error::BleError::NotSupported(format!("Failed to write document: {}", e))
145 })?;
146 Ok(())
147 }
148
149 fn load(&self) -> Result<Option<HiveDocument>> {
150 match std::fs::read(&self.path) {
151 Ok(data) => Ok(HiveDocument::decode(&data)),
152 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
153 Err(e) => Err(crate::error::BleError::NotSupported(format!(
154 "Failed to read document: {}",
155 e
156 ))),
157 }
158 }
159
160 fn clear(&mut self) -> Result<()> {
161 match std::fs::remove_file(&self.path) {
162 Ok(()) => Ok(()),
163 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
164 Err(e) => Err(crate::error::BleError::NotSupported(format!(
165 "Failed to clear document: {}",
166 e
167 ))),
168 }
169 }
170}
171
172#[cfg(feature = "std")]
174pub struct SharedStore<S: DocumentStore> {
175 inner: Arc<RwLock<S>>,
176}
177
178#[cfg(feature = "std")]
179impl<S: DocumentStore> SharedStore<S> {
180 pub fn new(store: S) -> Self {
182 Self {
183 inner: Arc::new(RwLock::new(store)),
184 }
185 }
186}
187
188#[cfg(feature = "std")]
189impl<S: DocumentStore> Clone for SharedStore<S> {
190 fn clone(&self) -> Self {
191 Self {
192 inner: self.inner.clone(),
193 }
194 }
195}
196
197#[cfg(feature = "std")]
198impl<S: DocumentStore> DocumentStore for SharedStore<S> {
199 fn save(&mut self, doc: &HiveDocument) -> Result<()> {
200 let mut inner = self.inner.write().unwrap();
201 inner.save(doc)
202 }
203
204 fn load(&self) -> Result<Option<HiveDocument>> {
205 let inner = self.inner.read().unwrap();
206 inner.load()
207 }
208
209 fn clear(&mut self) -> Result<()> {
210 let mut inner = self.inner.write().unwrap();
211 inner.clear()
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218 use crate::NodeId;
219
220 #[test]
221 fn test_memory_store() {
222 let mut store = MemoryStore::new();
223
224 assert!(store.load().unwrap().is_none());
226 assert!(!store.has_document());
227
228 let doc = HiveDocument::new(NodeId::new(0x12345678));
230 store.save(&doc).unwrap();
231
232 let loaded = store.load().unwrap().unwrap();
234 assert_eq!(loaded.node_id.as_u32(), 0x12345678);
235 assert!(store.has_document());
236
237 store.clear().unwrap();
239 assert!(store.load().unwrap().is_none());
240 }
241
242 #[test]
243 fn test_file_store() {
244 let temp_dir = std::env::temp_dir();
245 let path = temp_dir.join("hive_test_doc.bin");
246
247 let _ = std::fs::remove_file(&path);
249
250 let mut store = FileStore::new(&path);
251
252 assert!(store.load().unwrap().is_none());
254
255 let mut doc = HiveDocument::new(NodeId::new(0xAABBCCDD));
257 doc.increment_counter();
258 store.save(&doc).unwrap();
259
260 let loaded = store.load().unwrap().unwrap();
262 assert_eq!(loaded.node_id.as_u32(), 0xAABBCCDD);
263 assert_eq!(loaded.counter.value(), 1);
264
265 store.clear().unwrap();
267 assert!(store.load().unwrap().is_none());
268 }
269
270 #[test]
271 fn test_shared_store() {
272 let store = MemoryStore::new();
273 let mut shared = SharedStore::new(store);
274
275 let doc = HiveDocument::new(NodeId::new(0x11111111));
276 shared.save(&doc).unwrap();
277
278 let shared2 = shared.clone();
280 let loaded = shared2.load().unwrap().unwrap();
281 assert_eq!(loaded.node_id.as_u32(), 0x11111111);
282 }
283}