ludusavi 0.18.0

Game save backup tool
use std::collections::{BTreeSet, HashMap};

use crate::{
    prelude::{app_dir, StrictPath},
    resource::{
        config::{Config, RootsConfig},
        manifest::ManifestUpdate,
        ResourceFile, SaveableResourceFile,
    },
};

#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Cache {
    #[serde(default)]
    pub migrations: Migrations,
    #[serde(default)]
    pub manifests: Manifests,
    #[serde(default)]
    pub roots: BTreeSet<RootsConfig>,
    #[serde(default)]
    pub backup: Backup,
    #[serde(default)]
    pub restore: Restore,
}

#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Migrations {
    #[serde(default)]
    pub adopted_cache: bool,
}

pub type Manifests = HashMap<String, Manifest>;

#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Manifest {
    #[serde(default)]
    pub etag: Option<String>,
    #[serde(default)]
    pub checked: Option<chrono::DateTime<chrono::Utc>>,
    #[serde(default)]
    pub updated: Option<chrono::DateTime<chrono::Utc>>,
}

#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Backup {
    #[serde(default)]
    pub recent_games: BTreeSet<String>,
}

#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Restore {
    #[serde(default)]
    pub recent_games: BTreeSet<String>,
}

impl ResourceFile for Cache {
    const FILE_NAME: &'static str = "cache.yaml";
}

impl SaveableResourceFile for Cache {}

impl Cache {
    pub fn migrate_config(mut self, config: &mut Config) -> Self {
        let mut updated = false;

        if !self.migrations.adopted_cache {
            let _ = StrictPath::from(app_dir())
                .joined(".flag_migrated_legacy_config")
                .remove();
            self.migrations.adopted_cache = true;
            updated = true;
        }

        if self.roots.is_empty() && !config.roots.is_empty() {
            self.add_roots(&config.roots);
            updated = true;
        }

        if updated {
            self.save();
            config.save();
        }

        self
    }

    pub fn update_manifest(&mut self, update: ManifestUpdate) {
        let mut cached = self.manifests.entry(update.url).or_insert_with(Default::default);
        cached.etag = update.etag;
        cached.checked = Some(update.timestamp);
        if update.modified {
            cached.updated = Some(update.timestamp);
        }
    }

    pub fn add_roots(&mut self, roots: &Vec<RootsConfig>) {
        for root in roots {
            if !self.has_root(root) {
                self.roots.insert(RootsConfig {
                    path: root.path.interpreted(),
                    store: root.store,
                });
            }
        }
    }

    pub fn has_root(&self, root: &RootsConfig) -> bool {
        self.roots
            .iter()
            .any(|x| x.path.interpret() == root.path.interpret() && x.store == root.store)
    }
}