// This file incorporates code from luctius/fomod: // https://github.com/luctius/fomod/ // Original license: MIT / Apache-2.0 use std::{fs, io, path::Path}; use log::debug; use serde::{Deserialize, Serialize}; use thiserror::Error; #[derive(Debug, Deserialize, PartialEq)] pub struct Info { #[serde(rename = "Name")] pub name: Option, #[serde(rename = "Description")] pub description: Option, #[serde(rename = "Version")] pub version: Option, #[serde(rename = "Author")] pub author: Option, #[serde(rename = "Website")] pub website: Option, #[serde(rename = "CategoryId")] 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")] pub module_name: String, #[serde(rename = "moduleImage")] pub module_image: Option, #[serde(rename = "moduleDependencies")] pub module_dependencies: Option, #[serde(rename = "requiredInstallFiles")] pub required_install_files: Option, #[serde(rename = "installSteps")] pub install_steps: Option, #[serde(rename = "conditionalFileInstalls")] pub conditional_file_installs: Option, } impl Config { pub fn load_from_file(path: impl AsRef) -> Result { debug!( "Loading FOmod config from {}", path.as_ref().to_string_lossy() ); let data = fs::read_to_string(path)?; let config = quick_xml::de::from_str(&data)?; Ok(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, )] pub enum OrderEnum { #[default] Ascending, Explicit, Descending, } #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PluginTypeEnum { Required, Optional, Recommended, NotUsable, CouldBeUsable, } #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PluginType { #[serde(rename = "@name")] pub name: PluginTypeEnum, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PluginTypeDescriptor { #[serde(rename = "$value")] pub value: PluginTypeDescriptorEnum, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PluginTypeDescriptorEnum { #[serde(rename = "dependencyType")] DependencyType(DependencyPluginType), #[serde(rename = "type")] PluginType(PluginType), } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DependencyPluginType { #[serde(rename = "defaultType")] pub default_type: PluginType, pub patterns: DependencyPatternList, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DependencyPatternList { pub pattern: Vec, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DependencyPattern { pub dependencies: Vec, #[serde(rename = "type")] pub typ: PluginType, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct StepList { #[serde(rename = "@order", default)] pub order: OrderEnum, #[serde(rename = "installStep")] pub install_step: Vec, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct InstallStep { #[serde(rename = "@name")] pub name: String, pub visible: Option, #[serde(rename = "optionalFileGroups")] pub optional_file_groups: GroupList, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ModuleDependency { #[serde(rename = "@operator")] #[serde(default)] pub operator: DependencyOperator, #[serde(rename = "$value")] pub list: Vec, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum CompositeDependency { #[serde(rename = "fileDependency")] File(FileDependency), #[serde(rename = "flagDependency")] Flag(FlagDependency), #[serde(rename = "gameDependency")] Game(VersionDependency), #[serde(rename = "fommDependency")] Fomm(VersionDependency), #[serde(rename = "dependencies")] Dependency(ModuleDependency), } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FlagDependency { #[serde(rename = "@flag")] pub flag: String, #[serde(rename = "@value")] pub value: String, } impl From for FlagDependency { fn from(scf: SetConditionFlag) -> Self { Self { flag: scf.name, value: scf.flag_value, } } } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct VersionDependency { #[serde(rename = "@version")] pub version: String, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FileDependency { #[serde(rename = "@file")] pub file_name: String, #[serde(rename = "@state")] pub state: DependencyState, } #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum DependencyState { Active, Inactive, Missing, } #[derive( Copy, Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash, )] pub enum DependencyOperator { #[default] And, Or, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FileList { #[serde(rename = "$value")] pub list: Option>, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum FileTypeEnum { #[serde(rename = "file")] File(FileType), #[serde(rename = "folder")] Folder(FileType), } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FileType { #[serde(rename = "@source")] pub source: String, #[serde(rename = "@destination")] pub destination: Option, #[serde(rename = "@alwaysInstall")] pub always_install: Option, #[serde(rename = "@installIfUsable", default = "false_bool")] pub install_if_usable: bool, #[serde(rename = "@priority")] pub priority: Option, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct GroupList { #[serde(rename = "@order", default)] pub order: OrderEnum, pub group: Vec, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Group { #[serde(rename = "@name")] pub name: String, #[serde(rename = "@type")] pub typ: GroupType, pub plugins: PluginList, } #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum GroupType { SelectAtLeastOne, SelectAtMostOne, SelectExactlyOne, SelectAll, SelectAny, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PluginList { #[serde(rename = "@order", default)] pub order: OrderEnum, pub plugin: Vec, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Plugin { #[serde(rename = "@name")] pub name: String, pub description: String, pub image: Option, pub files: Option, #[serde(rename = "conditionFlags")] pub condition_flags: Option, #[serde(rename = "typeDescriptor")] pub type_descriptor: Option, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Image { #[serde(rename = "@path")] pub path: String, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct HeaderImage { #[serde(rename = "@path")] pub path: Option, #[serde(rename = "@showImage", default = "false_bool")] pub show_image: bool, #[serde(rename = "@showFade", default = "false_bool")] pub show_fade: bool, pub height: Option, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ConditionFlagList { pub flag: Vec, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SetConditionFlag { #[serde(rename = "@name")] pub name: String, #[serde(rename = "$value")] pub flag_value: String, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ConditionalFileInstallList { pub patterns: ConditionalInstallPatternList, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ConditionalInstallPatternList { pub pattern: Vec, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ConditionalInstallPattern { pub dependencies: ModuleDependency, pub files: FileList, } fn false_bool() -> bool { false }