ACCOUNT CREATION AND LOGIN
This commit is contained in:
123
src/rsc.rs
123
src/rsc.rs
@@ -1,50 +1,103 @@
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::Write,
|
||||
path::Path,
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use directories_next::ProjectDirs;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DataDir {
|
||||
dirs: ProjectDirs,
|
||||
}
|
||||
|
||||
impl Default for DataDir {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
dirs: ProjectDirs::from("", "", "openworm").unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DataRsc: bitcode::Encode + bitcode::DecodeOwned + Default {
|
||||
fn path() -> &'static str;
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
impl DataDir {
|
||||
pub fn get(&self) -> &Path {
|
||||
self.dirs.data_local_dir()
|
||||
}
|
||||
|
||||
pub fn load<T: DataRsc>(&self) -> T {
|
||||
let path = self.get().join(T::path());
|
||||
match fs::read(path) {
|
||||
Ok(bytes) => match bitcode::decode(&bytes) {
|
||||
Ok(data) => data,
|
||||
Err(_) => todo!(),
|
||||
},
|
||||
Err(_) => T::default(),
|
||||
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::to_string(&self.val).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);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save<T: DataRsc>(&self, data: &T) {
|
||||
let dir = self.get();
|
||||
fs::create_dir_all(dir).unwrap();
|
||||
let mut file = File::create(dir.join(T::path())).unwrap();
|
||||
// TODO: used to use encode_into_std_write from bincode here
|
||||
let data = bitcode::encode(data);
|
||||
file.write_all(&data).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user