vedvaring/
lib.rs

1use serde::Serialize;
2use serde::de::DeserializeOwned;
3use std::fmt::Display;
4use std::ops::Deref;
5use std::path::PathBuf;
6use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
7use std::{fs, sync::Arc};
8
9#[derive(Clone, Debug)]
10pub struct Saved<T: FsTrait>(Arc<RwLock<T>>);
11
12impl<T: FsTrait> Saved<T> {
13    pub fn new(item: T) -> Self {
14        item.save();
15        Self(Arc::new(RwLock::new(item)))
16    }
17
18    pub fn load(id: T::Key) -> Option<Self> {
19        let item = T::load(id)?;
20        Some(Self(Arc::new(RwLock::new(item))))
21    }
22
23    pub fn read(&self) -> RwLockReadGuard<T> {
24        self.0.read().unwrap()
25    }
26
27    pub fn write(&self) -> MyWriteGuard<T> {
28        MyWriteGuard(self.0.write().unwrap())
29    }
30}
31
32/// Wrapper for writeguard which saves the item to disk when the writeguard goes out of scope.
33pub struct MyWriteGuard<'a, T: FsTrait>(RwLockWriteGuard<'a, T>);
34
35impl<'a, T: FsTrait> Drop for MyWriteGuard<'a, T> {
36    fn drop(&mut self) {
37        self.save();
38    }
39}
40
41impl<'a, T: FsTrait> Deref for MyWriteGuard<'a, T> {
42    type Target = T;
43
44    fn deref(&self) -> &Self::Target {
45        &self.0
46    }
47}
48
49pub trait FsTrait
50where
51    Self: DeserializeOwned + Serialize + Sized,
52{
53    type Key: Display;
54
55    fn item_id(&self) -> Self::Key;
56
57    fn crate_name() -> String {
58        std::env::current_exe()
59            .unwrap()
60            .file_stem()
61            .unwrap()
62            .to_str()
63            .unwrap()
64            .to_string()
65            .to_lowercase()
66    }
67
68    fn root() -> PathBuf {
69        dirs::data_local_dir().unwrap().join(Self::crate_name())
70    }
71
72    fn items_path() -> PathBuf {
73        let name = std::any::type_name::<Self>();
74        let path = Self::root().join(name);
75        fs::create_dir_all(&path).unwrap();
76        path
77    }
78
79    fn item_path(&self) -> PathBuf {
80        Self::items_path().join(self.item_id().to_string())
81    }
82
83    fn load(id: Self::Key) -> Option<Self> {
84        let path = Self::items_path().join(id.to_string());
85        if !path.exists() {
86            return None;
87        }
88
89        let s: String = fs::read_to_string(&path).unwrap();
90        let t: Self = serde_json::from_str(&s).unwrap();
91        Some(t)
92    }
93
94    fn save(&self) {
95        use std::io::Write;
96
97        let path = self.item_path();
98        let s: String = serde_json::to_string(self).unwrap();
99        let mut f = fs::File::create(&path).unwrap();
100        f.write_all(&s.as_bytes()).unwrap();
101    }
102}