bark/persist/adaptor/
filestore.rs1use std::path::{Path, PathBuf};
4
5use tokio::fs;
6
7use crate::persist::adaptor::{Query, Record, StorageAdaptor};
8
9use super::memory::MemoryStorageAdaptor;
10
11#[derive(Debug)]
12pub struct FileStorageAdaptor {
13 file_path: PathBuf,
14 data: MemoryStorageAdaptor,
15}
16
17impl FileStorageAdaptor {
18 pub async fn open(file_path: impl AsRef<Path>) -> anyhow::Result<Self> {
19 let file_path = file_path.as_ref().to_path_buf();
20
21 let data = match fs::read_to_string(&file_path).await {
22 Ok(contents) => {
23 let records = serde_json::from_str::<Vec<Record>>(&contents)?;
24
25 let mut data = MemoryStorageAdaptor::new();
26 for record in records {
27 data.put(record).await?;
28 }
29 data
30 }
31 Err(e) if e.kind() == std::io::ErrorKind::NotFound => MemoryStorageAdaptor::new(),
32 Err(e) => return Err(e.into()),
33 };
34
35 Ok(Self { file_path, data })
36 }
37
38 async fn persist(&self) -> anyhow::Result<()> {
39 let records = self.data.partitions().values()
40 .map(|p| p.values())
41 .flatten().collect::<Vec<_>>();
42 fs::write(&self.file_path, serde_json::to_string(&records)?).await?;
43 Ok(())
44 }
45}
46
47#[async_trait]
48impl StorageAdaptor for FileStorageAdaptor {
49 async fn put(&mut self, record: Record) -> anyhow::Result<()> {
50 self.data.put(record).await?;
51 self.persist().await
52 }
53
54 async fn get(&self, partition: u8, pk: &[u8]) -> anyhow::Result<Option<Record>> {
55 self.data.get(partition, pk).await
56 }
57
58 async fn delete(&mut self, partition: u8, pk: &[u8]) -> anyhow::Result<Option<Record>> {
59 let deleted_record = self.data.delete(partition, pk).await?;
60 self.persist().await?;
61 Ok(deleted_record)
62 }
63
64 async fn query(&self, query: Query) -> anyhow::Result<Vec<Record>> {
65 self.data.query(query).await
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72 use crate::persist::adaptor::test_suite;
73
74 #[tokio::test]
75 async fn file_adaptor_test_suite() {
76 let temp_dir = tempfile::tempdir().unwrap();
77 let mut storage = FileStorageAdaptor::open(temp_dir.path().join("test.json")).await.unwrap();
78 test_suite::run_all(&mut storage).await;
79 }
80}