104 lines
2.9 KiB
Rust
104 lines
2.9 KiB
Rust
use std::{
|
|
fs::{self, File},
|
|
io::Write,
|
|
path::PathBuf,
|
|
};
|
|
|
|
use directories_next::ProjectDirs;
|
|
|
|
#[derive(Clone)]
|
|
pub struct DataDir {
|
|
pub path: PathBuf,
|
|
}
|
|
|
|
impl DataDir {
|
|
pub fn new(dir: Option<&str>) -> Self {
|
|
let dirs = ProjectDirs::from("", "", "openworm").unwrap();
|
|
let mut path = dirs.data_local_dir().to_path_buf();
|
|
if let Some(dir) = dir {
|
|
path = path.join(dir);
|
|
}
|
|
Self { path }
|
|
}
|
|
}
|
|
|
|
pub trait DataRsc: serde::Serialize + Default {
|
|
fn name() -> &'static str;
|
|
fn path() -> String {
|
|
Self::name().to_string() + ".ron"
|
|
}
|
|
fn parse_version(text: &str, version: u32) -> Result<Self, String>;
|
|
fn version() -> u32;
|
|
}
|
|
|
|
pub struct DataGuard<T: DataRsc> {
|
|
path: PathBuf,
|
|
val: T,
|
|
}
|
|
|
|
impl DataDir {
|
|
pub fn load<T: DataRsc>(&self) -> DataGuard<T> {
|
|
let path = self.path.join(T::path());
|
|
let invalid = |info: &str| {
|
|
println!("warning: invalid config @ {path:?}: {info}");
|
|
DataGuard {
|
|
path: path.clone(),
|
|
val: T::default(),
|
|
}
|
|
};
|
|
match fs::read_to_string(&path) {
|
|
Ok(text) => {
|
|
let mut lines = text.lines();
|
|
let Some(first) = lines.next() else {
|
|
return invalid("empty file");
|
|
};
|
|
let version_str: String = first
|
|
.chars()
|
|
.skip_while(|c| *c != 'v')
|
|
.skip(1)
|
|
.take_while(|c| !c.is_whitespace())
|
|
.collect();
|
|
let Ok(version): Result<u32, _> = version_str.parse() else {
|
|
return invalid("invalid version");
|
|
};
|
|
let text: String = lines.collect();
|
|
match T::parse_version(&text, version) {
|
|
Ok(val) => DataGuard { path, val },
|
|
Err(e) => invalid(&e),
|
|
}
|
|
}
|
|
Err(_) => DataGuard {
|
|
path,
|
|
val: T::default(),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: DataRsc> std::ops::Deref for DataGuard<T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.val
|
|
}
|
|
}
|
|
|
|
impl<T: DataRsc> std::ops::DerefMut for DataGuard<T> {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.val
|
|
}
|
|
}
|
|
|
|
impl<T: DataRsc> Drop for DataGuard<T> {
|
|
fn drop(&mut self) {
|
|
let dir = self.path.parent().unwrap();
|
|
fs::create_dir_all(dir).unwrap();
|
|
let mut file = File::create(dir.join(T::path())).unwrap();
|
|
let ron = ron::ser::to_string_pretty(&self.val, ron::ser::PrettyConfig::new()).unwrap();
|
|
let data = format!("// v{}\n{}\n", T::version(), ron);
|
|
if let Err(e) = file.write_all(data.as_bytes()) {
|
|
println!("Failed to write config @ {:?}: {e}", self.path);
|
|
}
|
|
}
|
|
}
|