diff --git a/Cargo.lock b/Cargo.lock index 27c7d33..901a78c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -305,6 +305,7 @@ dependencies = [ "log", "quick-xml", "serde", + "thiserror", "toml", ] diff --git a/Cargo.toml b/Cargo.toml index 6592a43..32e5530 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ libloot = "0.29.0" log = "0.4.29" quick-xml = { version = "0.39.2", features = ["serde-types", "serialize"] } serde = { version = "1.0.228", features = ["derive"] } +thiserror = "2.0.18" toml = "1.0.3" diff --git a/src/fomod.rs b/src/fomod.rs index 61c5296..419d790 100644 --- a/src/fomod.rs +++ b/src/fomod.rs @@ -2,9 +2,10 @@ // https://github.com/luctius/fomod/ // Original license: MIT / Apache-2.0 -use std::{error::Error, fs, path::Path}; +use std::{fs, io, path::Path}; use serde::{Deserialize, Serialize}; +use thiserror::Error; #[derive(Debug, Deserialize, PartialEq)] pub struct Info { @@ -22,6 +23,15 @@ pub struct Info { pub category_id: Option, } +impl Info { + pub fn load_from_file(path: impl AsRef) -> Result { + let data = fs::read_to_string(path)?; + let config = quick_xml::de::from_str(&data)?; + + Ok(config) + } +} + #[derive(Debug, Deserialize, PartialEq)] pub struct Config { #[serde(rename = "moduleName")] @@ -44,7 +54,7 @@ pub struct Config { } impl Config { - pub fn load_from_file(path: impl AsRef) -> Result> { + pub fn load_from_file(path: impl AsRef) -> Result { let data = fs::read_to_string(path)?; let config = quick_xml::de::from_str(&data)?; @@ -52,6 +62,15 @@ impl Config { } } +#[derive(Error, Debug)] +pub enum FOModError { + #[error("Failed to read file")] + Io(#[from] io::Error), + + #[error("Failed to parse config")] + Parse(#[from] quick_xml::de::DeError), +} + #[derive( Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash, Default, )] diff --git a/src/load_order.rs b/src/load_order.rs index 137e0eb..1b35368 100644 --- a/src/load_order.rs +++ b/src/load_order.rs @@ -1,10 +1,15 @@ use std::{ error::Error, + io, path::{Path, PathBuf}, }; -use libloot::{Game, GameType}; +use libloot::{ + Game, GameType, + error::{GameHandleCreationError, LoadPluginsError, SortPluginsError}, +}; use log::trace; +use thiserror::Error; use crate::{ basic_types::{ModdedInstance, RootConfig}, @@ -17,7 +22,7 @@ pub struct LoadOrder { } impl LoadOrder { - pub fn new(install_dir: impl AsRef, game_type: GameType) -> Result> { + pub fn new(install_dir: impl AsRef, game_type: GameType) -> Result { Ok(Self { game: Game::with_local_path( game_type, @@ -32,7 +37,7 @@ impl LoadOrder { &mut self, root_config: &RootConfig, instance: &ModdedInstance, - ) -> Result<(), Box> { + ) -> Result<(), LoadOrderError> { for installed_mod in &instance.mods { let mod_config = root_config.get_mod_by_id(&installed_mod.mod_id()).unwrap(); let mod_source_root = root_config.get_mod_location(&mod_config); @@ -51,7 +56,7 @@ impl LoadOrder { Ok(()) } - pub fn add_plugins_from_install(&mut self) -> Result<(), Box> { + pub fn add_plugins_from_install(&mut self) -> Result<(), LoadOrderError> { let plugins: Vec<_> = walk_files_recursive(self.target.join("Data"))? .filter(|f| Self::is_plugin_file(f.path())) .map(|f| f.path()) @@ -69,7 +74,7 @@ impl LoadOrder { .is_some_and(|ext| ext == "esp" || ext == "esm" || ext == "esl") } - pub fn load_order(&self) -> Result, Box> { + pub fn load_order(&self) -> Result, LoadOrderError> { trace!("Generating new load order"); let all_plugins = self.game.loaded_plugins(); let plugins_names: Vec<&str> = all_plugins.iter().map(|e| e.name()).collect(); @@ -79,3 +84,18 @@ impl LoadOrder { Ok(sorted) } } + +#[derive(Error, Debug)] +pub enum LoadOrderError { + #[error("Failed to read game directory")] + Io(#[from] io::Error), + + #[error("Failed to create libloot game")] + Creating(#[from] GameHandleCreationError), + + #[error("Failed to add plugins to libloot")] + Loading(#[from] LoadPluginsError), + + #[error("libloot failed to sort plugins")] + Sorting(#[from] SortPluginsError), +}