oliver 0.5.1

Lightweight CLI mod development tool for Baldur's Gate 3 on Linux
Documentation
use crate::read_only_guard::ReadOnlyGuard;
use anyhow::Result;
use larian_formats::{bg3::LoadOrder, error::Error, lspk::Lspk};
use std::{
    fs::ReadDir,
    io::{Read, Seek, Write},
    path::{Path, PathBuf},
};

pub const DEFAULT_APP_DATA_DIR: &str =
    "~/.local/share/Steam/steamapps/compatdata/1086940/pfx/drive_c/users/steamuser/AppData";

#[derive(Debug)]
pub struct Settings {
    mods_path: PathBuf,
    pub(crate) settings_path: PathBuf,
}

impl Default for Settings {
    fn default() -> Self {
        Self::with_app_data_dir(DEFAULT_APP_DATA_DIR)
    }
}

impl Settings {
    pub fn with_app_data_dir(path: &str) -> Self {
        let app_data_dir = match std::env::var("HOME") {
            Ok(home_dir) if path.starts_with('~') => path.replacen('~', &home_dir, 1),
            _ => path.into(),
        };

        let settings_path = [
            &app_data_dir,
            "Local",
            "Larian Studios",
            "Baldur's Gate 3",
            "PlayerProfiles",
            "Public",
            "modsettings.lsx",
        ]
        .into_iter()
        .collect();

        let mods_path = [
            &app_data_dir,
            "Local",
            "Larian Studios",
            "Baldur's Gate 3",
            "Mods",
        ]
        .into_iter()
        .collect();

        Self {
            mods_path,
            settings_path,
        }
    }

    // pub fn get_installed_mod_info(&self) -> Result<ModSet> {
    //     self.get_installed_mod_info_helper()
    // }

    // pub fn set_load_order(&self, order: LoadOrder) -> Result<()> {
    //     self.set_load_order_helper(order)
    // }

    pub fn install_all(
        &self,
        verbose: bool,
        dry_run: bool,
        refresh: bool,
        mod_file_paths: Vec<impl AsRef<Path>>,
    ) -> Result<()> {
        self.install_all_inner(verbose, dry_run, refresh, mod_file_paths)
    }

    pub fn uninstall_all(&self, dry_run: bool, ordered_indexes: Vec<usize>) -> Result<()> {
        self.uninstall_all_helper(dry_run, ordered_indexes)
    }

    pub fn import(&self, source: &Path) -> Result<()> {
        self.import_helper(source)
    }

    pub fn export(&self, destination: &Path) -> Result<()> {
        self.export_helper(destination)
    }

    pub fn pack(mod_files_root: PathBuf, destination: Option<PathBuf>) -> Result<()> {
        Self::pack_helper(mod_files_root, destination)
    }

    pub fn unpack(mod_file_path: &Path, destination: Option<PathBuf>) -> Result<()> {
        Self::unpack_helper(mod_file_path, destination)
    }

    pub fn dump(path: PathBuf) -> Result<()> {
        Self::dump_helper(path)
    }

    pub fn parse(paths: impl IntoIterator<Item = PathBuf>, verbose: bool) -> Result<()> {
        Self::parse_helper(paths, verbose)
    }

    pub fn get_mod_file_path(&self, mod_file_name: impl AsRef<Path>) -> PathBuf {
        self.mods_path.join(mod_file_name)
    }

    pub fn read_mod_file(&self, mod_file_name: impl AsRef<Path>) -> Result<Lspk> {
        let lspk = Lspk::from_file(self.get_mod_file_path(mod_file_name))?;
        Ok(lspk)
    }

    pub fn read_mod_files_dir(&self) -> Result<ReadDir> {
        let entries = std::fs::read_dir(&self.mods_path)?;
        Ok(entries)
    }

    pub fn open_mod_settings_file(&self) -> Result<impl Read + Write + Seek> {
        ReadOnlyGuard::new(&self.settings_path, false)
    }

    pub fn get_load_order(&self) -> Result<LoadOrder> {
        let mod_settings = self.open_mod_settings_file()?;
        let order = LoadOrder::try_parse(mod_settings).ok_or(Error::InvalidMetadataFile)?;

        Ok(order)
    }
}