vedvaring/
lib.rs

1use serde::{Deserialize, Serialize};
2use std::fs::{read_to_string, File};
3use std::io::Read;
4use std::path::PathBuf;
5
6pub trait Persist: for<'a> Deserialize<'a> + Serialize {
7    fn name(&self) -> String;
8    fn dir_name() -> String;
9
10    fn dir() -> PathBuf {
11        let p = home::home_dir()
12            .unwrap()
13            .join(format!(".local/share/{}", Self::dir_name()));
14        std::fs::create_dir_all(p.as_path()).unwrap();
15        p
16    }
17
18    fn path(&self) -> PathBuf {
19        let mut x = Self::dir().join(self.name());
20        x.set_extension("toml");
21        x
22    }
23
24    fn save(&self) {
25        std::fs::create_dir_all(Self::dir()).unwrap();
26        let s = toml::to_string(self).unwrap();
27        std::fs::write(self.path().as_path(), s).unwrap();
28    }
29
30    fn delete(self) {
31        std::fs::remove_file(self.path()).unwrap();
32    }
33
34    fn open(&self) {
35        std::process::Command::new("open")
36            .arg(self.path())
37            .status()
38            .expect("Failed to open file");
39    }
40
41    fn load_all() -> Vec<Self> {
42        let mut instances = Vec::new();
43        let paths = std::fs::read_dir(Self::dir()).expect("Failed to read directory");
44
45        for path in paths {
46            let path = path.expect("Failed to read path").path();
47            if path.extension().map(|ext| ext == "toml").unwrap_or(false) {
48                let mut file = File::open(&path).expect("Failed to open file");
49                let mut contents = String::new();
50                file.read_to_string(&mut contents)
51                    .expect("Failed to read file");
52
53                if let Ok(instance) = toml::from_str(&contents) {
54                    instances.push(instance);
55                }
56            }
57        }
58
59        instances
60    }
61}
62
63/// For singleton stuff, like config files, they need to implement default so
64/// a new one is created at first load.
65pub trait SingletonPersist: for<'a> Deserialize<'a> + Serialize + Default {
66    fn name() -> String;
67    fn dir_name() -> String;
68    fn save(&self) {
69        std::fs::create_dir_all(Self::dir()).unwrap();
70        let s = toml::to_string(self).unwrap();
71        std::fs::write(Self::path().as_path(), s).unwrap();
72    }
73
74    fn delete(self) {
75        std::fs::remove_file(Self::path()).unwrap();
76    }
77
78    fn open() {
79        std::process::Command::new("open")
80            .arg(Self::path())
81            .status()
82            .expect("Failed to open file");
83    }
84
85    fn dir() -> PathBuf {
86        let p = home::home_dir()
87            .unwrap()
88            .join(format!(".local/share/{}", Self::dir_name()));
89        std::fs::create_dir_all(p.as_path()).unwrap();
90        p
91    }
92
93    fn path() -> PathBuf {
94        let mut x = Self::dir().join(Self::name());
95        x.set_extension("toml");
96        x
97    }
98
99    fn load() -> Self {
100        let path = Self::path();
101        if !path.exists() {
102            Self::default().save();
103        }
104        let content = read_to_string(path).unwrap();
105        toml::from_str(content.as_str()).unwrap()
106    }
107}