use std::{
env::current_dir,
path::{Path, PathBuf},
sync::OnceLock,
};
use anyhow::anyhow;
use tokio::{
fs::{create_dir_all, read_to_string, remove_file, try_exists, write},
sync::RwLock,
};
use crate::{Creeper, Package, lock::Lock};
pub struct GameManager {
dir: OnceLock<PathBuf>,
pack: OnceLock<Package>,
lock: RwLock<OnceLock<Option<Lock>>>,
}
impl GameManager {
pub fn new(dir: Option<PathBuf>) -> Self {
let d = OnceLock::new();
if let Some(dir) = dir {
d.set(dir).unwrap();
}
Self {
dir: d,
pack: OnceLock::new(),
lock: RwLock::new(OnceLock::new()),
}
}
async fn find_dir(start: impl AsRef<Path>) -> anyhow::Result<PathBuf> {
let mut curr = start.as_ref().to_path_buf();
loop {
if try_exists(curr.join("creeper.toml")).await? {
break Ok(curr);
}
let parent = curr.parent().ok_or(anyhow!("not in any game instance"))?;
curr = parent.into();
}
}
pub async fn dir(&self) -> anyhow::Result<&PathBuf> {
if let Some(dir) = self.dir.get() {
return Ok(dir);
}
let found = Self::find_dir(current_dir()?).await?;
Ok(self.dir.get_or_init(|| found))
}
pub async fn pack_path(&self) -> anyhow::Result<PathBuf> {
let dir = self.dir().await?;
Ok(dir.join("creeper.toml"))
}
pub async fn lock_path(&self) -> anyhow::Result<PathBuf> {
let dir = self.dir().await?;
Ok(dir.join("creeper.lock"))
}
pub async fn pack(&self) -> anyhow::Result<&Package> {
if let Some(pack) = self.pack.get() {
return Ok(pack);
}
let toml = read_to_string(self.pack_path().await?).await?;
let pack = toml::from_str(&toml)?;
Ok(self.pack.get_or_init(|| pack))
}
pub async fn lock(&self) -> anyhow::Result<Option<Lock>> {
if let Some(lock) = self.lock.read().await.get() {
return Ok(lock.clone());
}
let path = self.lock_path().await?;
let lock = if try_exists(path).await? {
let toml = read_to_string(self.lock_path().await?).await?;
Some(toml::from_str(&toml)?)
} else {
None
};
let lock = self.lock.write().await.get_or_init(|| lock).clone();
Ok(lock)
}
pub async fn set_lock(&self, lock: Option<Lock>) -> anyhow::Result<()> {
*self.lock.write().await = lock.clone().into();
let path = self.lock_path().await?;
if let Some(lock) = lock {
let toml = toml::to_string(&lock)?;
create_dir_all(path.parent().unwrap()).await?;
write(&path, toml).await?;
} else {
if try_exists(&path).await? {
remove_file(&path).await?;
}
}
Ok(())
}
}
impl Creeper {
pub async fn game_dir(&self) -> anyhow::Result<&PathBuf> {
self.game.dir().await
}
pub async fn game_pack(&self) -> anyhow::Result<&Package> {
self.game.pack().await
}
pub async fn game_lock(&self) -> anyhow::Result<Option<Lock>> {
self.game.lock().await
}
pub async fn set_game_lock(&self, lock: Option<Lock>) -> anyhow::Result<()> {
self.game.set_lock(lock).await
}
}