ajour_core/fs/
save.rs

1use super::{config_dir, FilesystemError, Result};
2use serde::{de::DeserializeOwned, Serialize};
3
4use std::fs;
5use std::path::PathBuf;
6
7/// Defines a serializable struct that should persist on the filesystem inside the
8/// Ajour config directory.
9pub trait PersistentData: DeserializeOwned + Serialize {
10    /// Only method required to implement PersistentData on an object. Always relative to
11    /// the config folder for Ajour.
12    fn relative_path() -> PathBuf;
13
14    /// Returns the full file path. Will create any parent directories that don't
15    /// exist.
16    fn path() -> Result<PathBuf> {
17        let path = config_dir().join(Self::relative_path());
18
19        if let Some(dir) = path.parent() {
20            std::fs::create_dir_all(dir)?;
21        }
22
23        Ok(path)
24    }
25
26    /// Load from `PersistentData::path()`.
27    fn load() -> Result<Self> {
28        let path = Self::path()?;
29
30        if path.exists() {
31            let file = fs::File::open(&path)?;
32
33            Ok(serde_yaml::from_reader(&file)?)
34        } else {
35            Err(FilesystemError::FileDoesntExist { path })
36        }
37    }
38
39    /// Load from `PersistentData::path()`. If file doesn't exist, save it to the filesystem as `Default`
40    /// and return that object.
41    fn load_or_default<T: PersistentData + Default>() -> Result<T> {
42        let load_result = <T as PersistentData>::load();
43
44        match load_result {
45            Ok(deser) => Ok(deser),
46            _ => Ok(get_default_and_save()?),
47        }
48    }
49
50    /// Save to `PersistentData::path()`
51    fn save(&self) -> Result<()> {
52        let contents = serde_yaml::to_string(&self)?;
53
54        fs::write(Self::path()?, contents)?;
55
56        Ok(())
57    }
58}
59
60/// Get `Default` and save it.
61fn get_default_and_save<T: PersistentData + Default>() -> Result<T> {
62    let data = Default::default();
63
64    <T as PersistentData>::save(&data)?;
65
66    Ok(data)
67}