use crate::{VERSION, built_info};
use anyhow::{Context, Result};
use chrono::{DateTime, Duration, Utc};
use derive_more::{Deref, DerefMut};
use semver::Version;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use tokio::fs;
#[derive(Debug, Deserialize, Serialize)]
pub struct GlobalConfig {
pub system: String,
}
impl Default for GlobalConfig {
fn default() -> Self {
Self {
system: built_info::CFG_OS.into(),
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct LauncherConfig {
pub global: GlobalConfig,
pub launcher: AppState,
#[serde(default)]
pub player: AppState,
#[serde(default)]
pub workbench: AppState,
}
impl Default for LauncherConfig {
fn default() -> Self {
Self {
global: GlobalConfig::default(),
launcher: AppState {
version: Some(VERSION.clone()),
last_check: None,
},
player: AppState {
version: None,
last_check: None,
},
workbench: AppState {
version: None,
last_check: None,
},
}
}
}
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct AppState {
pub version: Option<Version>,
pub last_check: Option<DateTime<Utc>>,
}
impl AppState {
pub fn is_update_required(&self) -> bool {
self.is_not_exist() || self.is_allowed_to_check()
}
pub fn is_not_exist(&self) -> bool {
self.version.is_none()
}
pub fn is_allowed_to_check(&self) -> bool {
let deadline = Utc::now() - Duration::days(1);
match &self.last_check {
None => true,
Some(last) if last <= &deadline => true,
Some(_recent) => false,
}
}
pub fn update_check(&mut self) {
self.last_check = Some(Utc::now());
}
pub fn is_outdated(&self, recent_version: Version) -> bool {
if let Some(current_version) = self.version.as_ref() {
current_version < &recent_version
} else {
true
}
}
}
#[derive(Debug, Deref, DerefMut)]
pub struct Cacher {
cache_dir: PathBuf,
bin_dir: PathBuf,
state_path: PathBuf,
#[deref]
#[deref_mut]
state: LauncherConfig,
}
impl Cacher {
pub async fn create() -> Result<Self> {
let mut cache_dir = dirs::cache_dir().context("Cache directory is not available")?;
cache_dir.push("knowledge");
let mut bin_dir = cache_dir.clone();
bin_dir.push("bin");
let mut state_path = cache_dir.clone();
state_path.push("launcher.toml");
let state = LauncherConfig::default();
Ok(Self {
cache_dir,
bin_dir,
state_path,
state,
})
}
pub async fn initialize(&mut self) -> Result<()> {
fs::create_dir_all(&self.bin_dir).await?;
if let Ok(contents) = fs::read_to_string(&self.state_path).await {
if let Ok(state) = toml::from_str(&contents) {
self.state = state;
}
}
self.state.launcher.version = Some(VERSION.clone());
self.write_state().await?;
Ok(())
}
pub fn bin_dir(&self) -> &PathBuf {
&self.bin_dir
}
pub async fn write_state(&mut self) -> Result<()> {
let contents = toml::to_string(&self.state)?;
fs::write(&self.state_path, contents).await?;
Ok(())
}
pub async fn remove_cache(self) -> Result<()> {
fs::remove_dir_all(self.cache_dir).await?;
Ok(())
}
}