use serde::Serialize;
use serde::de::DeserializeOwned;
use std::collections::HashMap;
use std::fmt::Display;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::path::PathBuf;
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::{fs, sync::Arc};
pub trait DefaultWithId: FsTrait {
fn default_with_id(id: Self::Key) -> Self;
}
#[derive(Debug)]
pub struct StoredProvider<Raw: FsTrait, T: StoredTrait<Raw, S>, S = ()> {
inner: Arc<RwLock<HashMap<Raw::Key, Stored<S, Raw, T>>>>,
state: Arc<S>,
}
impl<Raw: FsTrait, T: StoredTrait<Raw, S>, S> StoredProvider<Raw, T, S> {
pub fn new(state: impl Into<Arc<S>>) -> Self {
let state = state.into();
let path = Raw::items_path();
let mut out: HashMap<Raw::Key, Stored<S, Raw, T>> = Default::default();
for entry in fs::read_dir(&path).unwrap() {
let path = entry.unwrap().path();
let s: String = fs::read_to_string(&path).unwrap();
let s: Raw = serde_json::from_str(&s).unwrap();
let key = s.item_id();
let s = Stored::new(s, state.clone());
out.insert(key, s);
}
Self {
inner: Arc::new(RwLock::new(out)),
state: state.into(),
}
}
pub fn load_all(&self) -> Vec<Stored<S, Raw, T>> {
self.inner.read().unwrap().values().cloned().collect()
}
pub fn load(&self, id: Raw::Key) -> Option<Stored<S, Raw, T>> {
let guard = self.inner.read().unwrap();
let val = guard.get(&id);
if val.is_some() {
return val.cloned();
}
drop(guard);
let val = Stored::load(id.clone(), self.state.clone())?;
let mut guard = self.inner.write().unwrap();
guard.insert(id, val.clone());
Some(val)
}
}
#[derive(Debug)]
pub struct Stored<S, Raw: FsTrait, T: StoredTrait<Raw, S>> {
inner: Arc<RwLock<T>>,
_phantom: PhantomData<Raw>,
_ph2: PhantomData<S>,
}
impl<S, Raw: FsTrait, T: StoredTrait<Raw, S>> Clone for Stored<S, Raw, T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_phantom: PhantomData,
_ph2: PhantomData,
}
}
}
impl<S, Raw: FsTrait, T: StoredTrait<Raw, S>> Stored<S, Raw, T> {
pub fn new(raw: Raw, state: impl Into<Arc<S>>) -> Self {
let state: Arc<S> = state.into();
let item = T::into_item(&raw, &state);
Self {
inner: Arc::new(RwLock::new(item)),
_phantom: PhantomData,
_ph2: PhantomData,
}
}
pub fn load(id: Raw::Key, state: impl Into<Arc<S>>) -> Option<Self> {
let state: Arc<S> = state.into();
let raw = Raw::load(id)?;
let item = T::into_item(&raw, &state);
Some(Self {
_phantom: PhantomData,
inner: Arc::new(RwLock::new(item)),
_ph2: PhantomData,
})
}
pub fn read(&self) -> RwLockReadGuard<T> {
self.inner.try_read().unwrap()
}
pub fn write(&self) -> StoredWriteGuard<T, Raw, S> {
StoredWriteGuard {
guard: self.inner.try_write().unwrap(),
_ph1: PhantomData,
_ph2: PhantomData,
}
}
}
pub struct StoredWriteGuard<'a, T: StoredTrait<Raw, S>, Raw: FsTrait, S> {
guard: RwLockWriteGuard<'a, T>,
_ph1: PhantomData<Raw>,
_ph2: PhantomData<S>,
}
impl<'a, T: StoredTrait<Raw, S>, Raw: FsTrait, S> Drop for StoredWriteGuard<'a, T, Raw, S> {
fn drop(&mut self) {
self.guard.into_raw().save().unwrap();
}
}
pub trait StoredTrait<Raw: FsTrait, S> {
fn into_item(raw: &Raw, state: &S) -> Self;
fn into_raw(&self) -> Raw;
}
#[derive(Debug)]
pub struct Saved<T: FsTrait> {
inner: Arc<RwLock<T>>,
}
impl<T: Display + FsTrait> Display for Saved<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = self.inner.try_read().unwrap().to_string();
write!(f, "{s}")
}
}
impl<T: FsTrait + DefaultWithId> Saved<T> {
pub fn load_or_create(id: T::Key) -> Self {
let x = match Self::load(id.clone()) {
Some(item) => item,
None => Saved::new(T::default_with_id(id)),
};
x
}
}
impl<T: FsTrait> Saved<T> {
pub fn new(item: T) -> Self {
item.save().unwrap();
Self {
inner: Arc::new(RwLock::new(item)),
}
}
pub fn load_all() -> Vec<Self> {
T::load_all()
.into_iter()
.map(|item| Self {
inner: Arc::new(RwLock::new(item)),
})
.collect()
}
pub fn load(id: T::Key) -> Option<Self> {
let item = T::load(id)?;
Some(Self {
inner: Arc::new(RwLock::new(item)),
})
}
pub fn read(&self) -> RwLockReadGuard<T> {
self.inner.try_read().unwrap()
}
pub fn write(&self) -> MyWriteGuard<T> {
MyWriteGuard(self.inner.try_write().unwrap())
}
}
impl<T: FsTrait> Clone for Saved<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
pub struct MyWriteGuard<'a, T: FsTrait>(RwLockWriteGuard<'a, T>);
impl<'a, T: FsTrait> Drop for MyWriteGuard<'a, T> {
fn drop(&mut self) {
self.save().unwrap();
}
}
impl<'a, T: FsTrait> Deref for MyWriteGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a, T: FsTrait> DerefMut for MyWriteGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Debug)]
pub enum SaveError {
Serialize(serde_json::Error),
Io(std::io::Error),
}
use std::hash::Hash;
pub trait FsTrait
where
Self: DeserializeOwned + Serialize + Sized,
{
type Key: Display + Clone + Hash + PartialEq + Eq;
fn item_id(&self) -> Self::Key;
fn crate_name() -> String {
std::env::current_exe()
.unwrap()
.file_stem()
.unwrap()
.to_str()
.unwrap()
.to_string()
.to_lowercase()
}
fn root() -> PathBuf {
dirs::data_local_dir().unwrap().join(Self::crate_name())
}
fn items_path() -> PathBuf {
let name = std::any::type_name::<Self>();
let path = Self::root().join(name);
fs::create_dir_all(&path).unwrap();
path
}
fn item_path(&self) -> PathBuf {
Self::items_path().join(self.item_id().to_string())
}
fn load_all() -> Vec<Self> {
let path = Self::items_path();
let mut out: Vec<Self> = vec![];
for entry in fs::read_dir(&path).unwrap() {
let path = entry.unwrap().path();
let s: String = fs::read_to_string(&path).unwrap();
let s: Self = serde_json::from_str(&s).unwrap();
out.push(s);
}
out
}
fn load(id: Self::Key) -> Option<Self> {
let path = Self::items_path().join(id.to_string());
if !path.exists() {
return None;
}
let s: String = fs::read_to_string(&path).unwrap();
let t: Self = serde_json::from_str(&s).unwrap();
Some(t)
}
fn delete(id: Self::Key) {
let path = Self::items_path().join(id.to_string());
fs::remove_file(&path).unwrap();
}
fn save(&self) -> Result<(), SaveError> {
use std::io::Write;
let path = self.item_path();
let s = serde_json::to_string_pretty(self).map_err(SaveError::Serialize)?;
let mut f = fs::File::create(&path).map_err(SaveError::Io)?;
f.write_all(s.as_bytes()).map_err(SaveError::Io)?;
Ok(())
}
}