use crate::Settings;
use anyhow::{anyhow, Result};
use larian_formats::bg3::{
raw::{ModuleInfoNode, ModulesChildren},
ModuleInfo,
};
use std::{collections::HashMap, path::PathBuf};
impl Settings {
pub(crate) fn uninstall_all_helper(
&self,
dry_run: bool,
mut ordered_indexes: Vec<usize>,
) -> Result<()> {
let mut save = self.read_current_installed_mods()?;
let Some(order_node) = save.find_node_by_id("ModOrder")?.children.first() else {
return Ok(());
};
let mods_node = save.find_node_by_id("Mods")?;
let unordered_entries: Result<HashMap<_, _>> = ordered_indexes
.iter()
.copied()
.filter_map(|ordered_index| {
get_unordered_index(order_node, ordered_index, mods_node)
.map(|possible_info| {
possible_info.map(|(info, index)| (info.uuid.clone(), (info, index)))
})
.transpose()
})
.collect();
let unordered_entries = unordered_entries?;
let mod_files_to_delete: Result<Vec<_>> = self
.read_mod_files_dir()?
.map(|e| {
let entry = e?;
if !entry.file_type()?.is_file() {
return Ok(None);
}
let file_name = PathBuf::from(entry.file_name());
let extension = file_name.extension();
if !extension.map_or(false, |e| e.eq_ignore_ascii_case("pak")) {
return Ok(None);
}
let pak = self
.read_mod_file(&file_name)?
.read()?
.extract_meta_lsx()?
.deserialize_as_mod_pak()?;
Ok(unordered_entries
.get(pak.module_info.uuid.as_str())
.map(|(info, index)| (info, index, file_name)))
})
.collect();
let mod_files_to_delete = mod_files_to_delete?;
let mut indexes_to_delete = Vec::with_capacity(mod_files_to_delete.len());
for mod_file_info in mod_files_to_delete {
let Some((info, index, file_name)) = mod_file_info else {
continue;
};
if dry_run {
println!("{} would be uninstalled with a non-dry run", info.name);
} else {
println!("uninstalling {}", info.name);
let mod_file = self.get_mod_file_path(file_name);
std::fs::remove_file(mod_file)?;
}
indexes_to_delete.push(*index);
}
if !dry_run {
indexes_to_delete.sort_unstable_by_key(|i| usize::MAX - i);
let mods_node = save.find_node_mut_by_id("Mods")?;
for i in indexes_to_delete {
mods_node.children[0].node.remove(i);
}
let order_node = save.find_node_mut_by_id("ModOrder")?;
ordered_indexes.sort_unstable_by_key(|i| usize::MAX - i);
for i in ordered_indexes {
order_node.children[0].node.remove(i);
}
self.write_settings_file(&save)?;
}
Ok(())
}
}
fn get_unordered_index<'a>(
ordered: &'a ModulesChildren,
mod_index: usize,
mods: &'a ModuleInfoNode,
) -> Result<Option<(ModuleInfo, usize)>> {
let Some(order_entry) = ordered.node.get(mod_index) else {
return Ok(None);
};
let uuid = order_entry
.get_attribute_value_where_id(|id| id == "UUID")
.ok_or_else(|| anyhow!("missing UUID for mod order index {mod_index}"))?
.to_string();
let Some(index) = mods.find_index_where_child_id(&uuid) else {
return Ok(None);
};
let pak = mods.children[0].node[index].clone().try_into()?;
Ok(Some((pak, index)))
}