112 lines
2.7 KiB
Rust
112 lines
2.7 KiB
Rust
use std::io;
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
|
|
use serde::{de::DeserializeOwned, Serialize};
|
|
use tokio::{
|
|
fs::{self, File},
|
|
io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
|
|
};
|
|
|
|
#[derive(Clone)]
|
|
pub struct Persistence {
|
|
state: Arc<PersistenceState>,
|
|
}
|
|
|
|
pub struct PersistenceFile<S> {
|
|
content: S,
|
|
file: Option<File>,
|
|
}
|
|
|
|
enum PersistenceState {
|
|
NoPersistence,
|
|
Persistence { directory: PathBuf },
|
|
}
|
|
|
|
impl Persistence {
|
|
pub async fn new() -> Persistence {
|
|
Persistence {
|
|
state: Arc::new(PersistenceState::NoPersistence),
|
|
}
|
|
}
|
|
|
|
pub async fn new_persistence(directory: PathBuf) -> io::Result<Persistence> {
|
|
let _ = fs::read_dir(&directory).await?;
|
|
|
|
Ok(Persistence {
|
|
state: Arc::new(PersistenceState::Persistence { directory }),
|
|
})
|
|
}
|
|
|
|
pub async fn open<S: Default + Serialize + DeserializeOwned>(
|
|
&self,
|
|
name: String,
|
|
) -> Result<PersistenceFile<S>, io::Error> {
|
|
Ok(match &*self.state {
|
|
PersistenceState::NoPersistence => PersistenceFile {
|
|
content: S::default(),
|
|
file: None,
|
|
},
|
|
PersistenceState::Persistence { directory } => {
|
|
let file_path = directory.join(&name);
|
|
let mut file = fs::OpenOptions::new()
|
|
.create(true)
|
|
.read(true)
|
|
.write(true)
|
|
.open(file_path)
|
|
.await?;
|
|
|
|
let mut content = String::new();
|
|
file.read_to_string(&mut content).await?;
|
|
|
|
let content = ron::from_str(&content).unwrap_or_else(|_| {
|
|
info!("Failed to load persistence {name}, using default");
|
|
S::default()
|
|
});
|
|
|
|
PersistenceFile {
|
|
content,
|
|
file: Some(file),
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<S> PersistenceFile<S>
|
|
where
|
|
S: Serialize,
|
|
{
|
|
pub fn get(&self) -> &S {
|
|
&self.content
|
|
}
|
|
|
|
pub async fn set(&mut self, s: S) -> anyhow::Result<()> {
|
|
self.content = s;
|
|
|
|
if let Some(file) = &mut self.file {
|
|
let serialized = ron::ser::to_string_pretty(&self.content, Default::default())?;
|
|
file.rewind().await?;
|
|
file.set_len(0).await?;
|
|
file.write_all(serialized.as_bytes()).await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn update(&mut self, f: impl FnOnce(&mut S)) -> anyhow::Result<()>
|
|
where
|
|
S: Clone + PartialEq,
|
|
{
|
|
let mut new = self.content.clone();
|
|
|
|
f(&mut new);
|
|
|
|
if new != self.content {
|
|
self.set(new).await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|