bark/persist/adaptor/
filestore.rs

1//! Simple file-based storage for testing, built on top of the memory storage adaptor.
2
3use 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}