Stuff
This commit is contained in:
6 files changed
+446
-233
No files matched your search
@@ -0,0 +1,111 @@
|
||||
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(())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user