use std::hash::{DefaultHasher, Hash, Hasher}; use std::time::SystemTime; use crate::model::config::GlobalConfig; use crate::model::patch::PatchFileVec; use crate::pkg::{Feature, Status}; use crate::profiles::types::Profile; use crate::{model::misc::Game, pkg::PkgKey}; use crate::pkg_store::PackageStore; use crate::util; use anyhow::{anyhow, Result}; use fern::colors::{Color, ColoredLevelConfig}; use tauri::AppHandle; pub struct GlobalState { pub remain_open: bool, } pub struct AppData { pub profile: Option, pub pkgs: PackageStore, pub cfg: GlobalConfig, pub state: GlobalState, pub patch_vec: PatchFileVec, } #[derive(PartialEq, Debug, Copy, Clone)] pub enum ToggleAction { Disable, EnableSelf, EnableRecursive, } impl AppData { pub fn new(apph: AppHandle) -> AppData { let cfg = std::fs::read_to_string(util::config_dir().join("config.json")) .and_then(|s| Ok(serde_json::from_str::(&s)?)) .unwrap_or_default(); Self::init_logger(&cfg); let profile = match cfg.recent_profile { Some((game, ref name)) => Profile::load(game, name.clone()).ok(), None => None }; let patch_vec = PatchFileVec::new(util::config_dir()) .map_err(|e| log::error!("unable to load patch set: {e}")) .unwrap_or_default(); log::info!("recent profile: {:?}", profile); AppData { profile: profile, pkgs: PackageStore::new(apph.clone()), cfg, state: GlobalState { remain_open: true }, patch_vec } } pub fn write(&self) -> Result<(), std::io::Error> { std::fs::write(util::config_dir().join("config.json"), serde_json::to_string_pretty(&self.cfg)?) } pub fn switch_profile(&mut self, game: Game, name: String) -> Result<()> { match Profile::load(game.clone(), name.clone()) { Ok(profile) => { self.profile = Some(profile); self.cfg.recent_profile = Some((game, name)); self.write()?; Ok(()) } Err(e) => { self.profile = None; self.cfg.recent_profile = None; Err(e) } } } pub fn toggle_package(&mut self, key: PkgKey, action: ToggleAction) -> Result<()> { log::debug!("toggle: {} {:?}", key, action); let profile = self.profile.as_mut().ok_or_else(|| anyhow!("No profile"))?; if action != ToggleAction::Disable { let pkg = self.pkgs.get(&key)?; let loc = pkg.loc .clone() .ok_or_else(|| anyhow!("Attempted to enable a non-existent package"))?; if let Status::OK(feature_set, _) = loc.status { if feature_set.contains(Feature::Mod) { profile.mod_pkgs_mut().insert(key); } } if action == ToggleAction::EnableRecursive { for d in &loc.dependencies { _ = self.toggle_package(d.clone(), action); } } } else { profile.mod_pkgs_mut().remove(&key); for (ckey, pkg) in self.pkgs.get_all() { if let Some(loc) = pkg.loc { if loc.dependencies.contains(&key) { self.toggle_package(ckey, action)?; } } } } Ok(()) } pub fn sum_packages(&self, p: &Profile) -> String { let mut hasher = DefaultHasher::new(); for key in p.mod_pkgs().into_iter() { if let Ok(pkg) = self.pkgs.get(&key) { if let Some(loc) = &pkg.loc { key.hash(&mut hasher); loc.version.hash(&mut hasher); } } } hasher.finish().to_string() } pub fn fix(&mut self) { if let Some(p) = &mut self.profile { p.fix(&self.pkgs); } } fn init_logger(cfg: &GlobalConfig) { let mut fern_builder; let colors = ColoredLevelConfig::new() .debug(Color::Green) .info(Color::Blue) .warn(Color::Yellow) .error(Color::Red); fern_builder = fern::Dispatch::new() .format(move |out, message, record| { out.finish(format_args!( "[{} {} {}] {}", humantime::format_rfc3339_seconds(SystemTime::now()), colors.color(record.level()), record.target(), message )) }) .chain(std::io::stdout()) .chain(fern::log_file(util::data_dir().join("log.txt")).expect("unable to initialize the logger")); if cfg.verbose == true { fern_builder = fern_builder.level(log::LevelFilter::Debug); } else { fern_builder = fern_builder.level(log::LevelFilter::Info); } if let Err(e) = fern_builder.apply() { panic!("unable to initialize the logger? {:?}", e); } } }