use crate::Settings;
use anyhow::Result;
use larian_formats::bg3::{raw::Save, ModuleInfo};
use std::collections::{HashMap, HashSet};
impl Settings {
pub(crate) fn get_installed_mod_info_helper(&self) -> Result<ModSet> {
let parsed_settings = self.read_current_installed_mods()?;
let installed_mod_info = get_installed_mod_info(&parsed_settings)?;
Ok(ModSet {
inner: installed_mod_info.collect(),
})
}
pub(crate) fn get_load_order_helper(&self) -> Result<LoadOrder> {
let parsed_settings = self.read_current_installed_mods()?;
let installed_mod_info = get_installed_mod_info(&parsed_settings)?;
let ordered_uuids = get_ordered_uuids(&parsed_settings)?;
get_ordered_mods(ordered_uuids, installed_mod_info)
}
}
#[derive(Debug, Clone)]
pub struct ModSet {
inner: HashSet<ModuleInfo>,
}
impl ModSet {
pub fn iter(&self) -> impl Iterator<Item = &ModuleInfo> {
self.inner.iter()
}
}
#[derive(Debug, Clone)]
pub struct LoadOrder {
ordered_mods: Vec<ModuleInfo>,
extras: ModSet,
}
impl LoadOrder {
pub fn ordered(&self) -> impl Iterator<Item = &ModuleInfo> {
self.ordered_mods.iter()
}
pub fn has_extras(&self) -> bool {
!self.extras.inner.is_empty()
}
pub fn extras(&self) -> impl Iterator<Item = &ModuleInfo> {
self.extras.iter()
}
pub fn order_len(&self) -> usize {
self.ordered_mods.len()
}
}
fn get_installed_mod_info(parsed_settings: &Save) -> Result<impl Iterator<Item = ModuleInfo> + '_> {
Ok(parsed_settings
.find_node_by_id("Mods")?
.children
.first()
.into_iter()
.flat_map(|c| {
c.node
.iter()
.cloned()
.filter_map(|n| ModuleInfo::try_from(n).ok())
}))
}
fn get_ordered_uuids(parsed_settings: &Save) -> Result<impl Iterator<Item = &str> + '_> {
Ok(parsed_settings
.find_node_by_id("ModOrder")?
.children
.first()
.into_iter()
.flat_map(|c| {
c.node
.iter()
.filter_map(|n| n.get_attribute_value_where_id(|id| id == "UUID"))
}))
}
fn get_ordered_mods<'a>(
ordered_uuids: impl Iterator<Item = &'a str>,
installed_mod_info: impl Iterator<Item = ModuleInfo>,
) -> Result<LoadOrder> {
let mut uuid_indexed_mods: HashMap<_, _> = installed_mod_info
.map(|mod_info| (mod_info.uuid.clone(), mod_info))
.collect();
let ordered_mods = ordered_uuids
.filter_map(|uuid| uuid_indexed_mods.remove(uuid))
.collect();
let extras = ModSet {
inner: uuid_indexed_mods.into_values().collect(),
};
Ok(LoadOrder {
ordered_mods,
extras,
})
}