ed_journals/modules/fs/models/common/
changed_json_file.rs1use crate::fs::common::json_file::JsonFile;
2use crate::fs::{LogFSError, Unblocker};
3use serde::de::DeserializeOwned;
4use std::path::Path;
5use std::sync::Arc;
6use twox_hash::XxHash64;
7
8pub struct ChangedJsonFile<R>
11where
12 R: DeserializeOwned,
13{
14 inner: JsonFile<R>,
15 last_hash: Option<u64>,
16}
17
18impl<R> ChangedJsonFile<R>
19where
20 R: DeserializeOwned + PartialEq,
21{
22 pub fn new<P: AsRef<Path>>(
25 path: P,
26 unblocker: impl Into<Arc<dyn Unblocker>>,
27 ) -> Result<ChangedJsonFile<R>, LogFSError> {
28 Ok(ChangedJsonFile {
29 inner: JsonFile::new(path, unblocker)?,
30 last_hash: None,
31 })
32 }
33
34 pub fn content(&mut self) -> Result<Option<R>, LogFSError> {
38 let bytes = self.inner.byte_content()?;
39 if bytes.is_empty() {
40 return Ok(None);
41 }
42
43 let hash = XxHash64::oneshot(0, &bytes);
44
45 if self.last_hash.is_some_and(|v| v == hash) {
46 return Ok(None);
47 }
48
49 self.last_hash = Some(hash);
50 Ok(Some(serde_json::from_slice(&bytes)?))
51 }
52
53 #[cfg(feature = "asynchronous")]
55 pub async fn content_async(&mut self) -> Result<Option<R>, LogFSError> {
56 let bytes = self.inner.byte_content_async().await?;
57 if bytes.is_empty() {
58 return Ok(None);
59 }
60
61 let hash = XxHash64::oneshot(0, &bytes);
62
63 if self.last_hash.is_some_and(|v| v == hash) {
64 return Ok(None);
65 }
66
67 self.last_hash = Some(hash);
68 Ok(Some(serde_json::from_slice(&bytes)?))
69 }
70}