Skip to main content

ed_journals/modules/fs/models/common/
json_file.rs

1use crate::fs::{FileWatcher, LogFSError, Unblocker};
2use serde::de::DeserializeOwned;
3use std::marker::PhantomData;
4use std::path::{Path, PathBuf};
5use std::sync::Arc;
6
7/// A wrapper around [FileWatcher] which can be used to read the contents of a JSON file.
8pub struct JsonFile<R>
9where
10    R: DeserializeOwned,
11{
12    path: PathBuf,
13    _w: FileWatcher,
14    _p: PhantomData<R>,
15}
16
17impl<R> JsonFile<R>
18where
19    R: DeserializeOwned,
20{
21    /// Creates a new [JsonFile] from the provided path and unblocker.
22    pub fn new<P: AsRef<Path>>(
23        path: P,
24        unblocker: impl Into<Arc<dyn Unblocker>>,
25    ) -> Result<JsonFile<R>, LogFSError> {
26        let path = path.as_ref();
27        let file_watcher = FileWatcher::new(path, unblocker)?;
28
29        Ok(JsonFile {
30            path: path.to_path_buf(),
31            _w: file_watcher,
32            _p: PhantomData,
33        })
34    }
35
36    /// Returns the current contents of the file as a raw byte vector.
37    pub fn byte_content(&self) -> Result<Vec<u8>, LogFSError> {
38        Ok(std::fs::read(&self.path)?)
39    }
40
41    /// Returns the current contents of the file as a string.
42    pub fn string_content(&self) -> Result<String, LogFSError> {
43        Ok(std::fs::read_to_string(&self.path)?)
44    }
45
46    /// Returns the current contents of the file as a deserialized object, or [None] if the file
47    /// is empty (which happens when the game clears the file before it starts to write.)
48    pub fn content(&self) -> Result<Option<R>, LogFSError> {
49        let contents = self.byte_content()?;
50
51        if contents.is_empty() {
52            return Ok(None);
53        }
54
55        Ok(Some(serde_json::from_slice(&contents)?))
56    }
57
58    #[cfg(feature = "asynchronous")]
59    pub async fn byte_content_async(&self) -> Result<Vec<u8>, LogFSError> {
60        Ok(async_fs::read(&self.path).await?)
61    }
62
63    #[cfg(feature = "asynchronous")]
64    pub async fn string_content_async(&self) -> Result<String, LogFSError> {
65        Ok(async_fs::read_to_string(&self.path).await?)
66    }
67
68    #[cfg(feature = "asynchronous")]
69    pub async fn content_async(&self) -> Result<Option<R>, LogFSError> {
70        let contents = self.byte_content_async().await?;
71
72        if contents.is_empty() {
73            return Ok(None);
74        }
75
76        Ok(Some(serde_json::from_slice(&contents)?))
77    }
78}