From fcc65f68bb1d9dee35d1de30ca7503501ac0c4f5 Mon Sep 17 00:00:00 2001 From: Niklas Kapelle Date: Fri, 20 Mar 2026 13:08:56 +0100 Subject: [PATCH] save Link as a different format in toml --- src/types/link.rs | 55 ++++++-- tests/data/instance_complex.toml | 232 +++++++++++-------------------- tests/modded_instance_test.rs | 4 +- 3 files changed, 124 insertions(+), 167 deletions(-) diff --git a/src/types/link.rs b/src/types/link.rs index 078ace1..4802edc 100644 --- a/src/types/link.rs +++ b/src/types/link.rs @@ -1,15 +1,16 @@ +use serde::{ + Deserialize, Deserializer, Serialize, Serializer, + de::{self, Visitor}, +}; use std::{ - fmt::Debug, + fmt::{self, Debug}, path::{Path, PathBuf}, }; -use serde::{Deserialize, Serialize}; - use crate::types::mod_file::ModFile; /// A link between a file from a mod and a destination in a ModdedInstance -#[derive(Clone, Deserialize, Serialize, PartialEq, Eq, Hash)] -#[serde(from = "(PathBuf, PathBuf)", into = "(PathBuf,PathBuf)")] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct Link { src: PathBuf, dst: PathBuf, @@ -36,18 +37,46 @@ impl Link { } } -impl From<(PathBuf, PathBuf)> for Link { - fn from(value: (PathBuf, PathBuf)) -> Self { - Self { - src: value.0, - dst: value.1, +impl Serialize for Link { + fn serialize(&self, serializer: S) -> Result { + if self.src == self.dst { + serializer.serialize_str(&self.src.to_string_lossy()) + } else { + serializer.serialize_str(&format!( + "{} -> {}", + self.src.to_string_lossy(), + self.dst.to_string_lossy() + )) } } } -impl From for (PathBuf, PathBuf) { - fn from(value: Link) -> Self { - (value.src, value.dst) +impl<'de> Deserialize<'de> for Link { + fn deserialize>(deserializer: D) -> Result { + struct LinkVisitor; + + impl<'de> Visitor<'de> for LinkVisitor { + type Value = Link; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(r#"a string like "src -> dst" or "path" if they are the same"#) + } + + fn visit_str(self, value: &str) -> Result { + match value.split_once(" -> ") { + Some((src, dst)) => Ok(Link { + src: PathBuf::from(src), + dst: PathBuf::from(dst), + }), + None => Ok(Link { + src: PathBuf::from(value), + dst: PathBuf::from(value), + }), + } + } + } + + deserializer.deserialize_str(LinkVisitor) } } diff --git a/tests/data/instance_complex.toml b/tests/data/instance_complex.toml index 3c0b05c..bbd693a 100644 --- a/tests/data/instance_complex.toml +++ b/tests/data/instance_complex.toml @@ -10,171 +10,99 @@ load_order = [ "ccBGSSSE037-Curios.esl", "ccBGSSSE025-AdvDSGS.esm", "_ResourcePack.esl", + "RaceMenu.esp", "SkyUI_SE.esp", + "RaceMenuPlugin.esp", ] -game_file_overrides = [[ - "skse64_loader.exe", - "SkyrimSELauncher.exe", -]] - -[[mods]] -id = "skyui" -files = [ - [ - "SkyUI_SE.esp", - "Data/SkyUI_SE.esp", -], - [ - "SkyUI_SE.bsa", - "Data/SkyUI_SE.bsa", -], +game_file_overrides = [ + "skse64_loader.exe -> SkyrimSELauncher.exe" ] -priority = 0 [[mods]] id = "skse" files = [ - [ - "Data/Scripts/actorbase.pex", - "Data/Scripts/actorbase.pex", -], - [ - "Data/Scripts/weather.pex", - "Data/Scripts/weather.pex", -], - [ - "skse64_loader.exe", - "skse64_loader.exe", -], - [ - "Data/Scripts/headpart.pex", - "Data/Scripts/headpart.pex", -], - [ + "Data/Scripts/math.pex", + "Data/Scripts/form.pex", "Data/Scripts/soulgem.pex", - "Data/Scripts/soulgem.pex", -], - [ - "Data/Scripts/modevent.pex", - "Data/Scripts/modevent.pex", -], - [ - "Data/Scripts/actorvalueinfo.pex", - "Data/Scripts/actorvalueinfo.pex", -], - [ - "Data/Scripts/book.pex", - "Data/Scripts/book.pex", -], - [ + "Data/Scripts/formlist.pex", + "Data/Scripts/stringutil.pex", + "Data/Scripts/colorcomponent.pex", + "Data/Scripts/quest.pex", + "Data/Scripts/faction.pex", + "Data/Scripts/combatstyle.pex", + "Data/Scripts/actorbase.pex", "Data/Scripts/potion.pex", - "Data/Scripts/potion.pex", -], - [ - "Data/Scripts/spell.pex", - "Data/Scripts/spell.pex", -], - [ - "Data/Scripts/perk.pex", - "Data/Scripts/perk.pex", -], - [ + "Data/Scripts/actor.pex", + "Data/Scripts/game.pex", + "Data/Scripts/armor.pex", + "Data/Scripts/headpart.pex", "Data/Scripts/objectreference.pex", - "Data/Scripts/objectreference.pex", -], + "Data/Scripts/weapon.pex", + "Data/Scripts/perk.pex", + "Data/Scripts/constructibleobject.pex", + "Data/Scripts/armoraddon.pex", + "Data/Scripts/textureset.pex", + "Data/Scripts/scroll.pex", + "Data/Scripts/actorvalueinfo.pex", + "Data/Scripts/equipslot.pex", + "Data/Scripts/art.pex", + "Data/Scripts/colorform.pex", + "Data/Scripts/weather.pex", + "Data/Scripts/gamedata.pex", + "Data/Scripts/skse.pex", + "Data/Scripts/sound.pex", + "Data/Scripts/formtype.pex", + "Data/Scripts/spawnertask.pex", + "Data/Scripts/netimmerse.pex", + "Data/Scripts/ingredient.pex", + "Data/Scripts/book.pex", + "Data/Scripts/ui.pex", + "Data/Scripts/leveleditem.pex", + "Data/Scripts/spell.pex", + "Data/Scripts/leveledspell.pex", + "Data/Scripts/modevent.pex", + "Data/Scripts/keyword.pex", + "Data/Scripts/activemagiceffect.pex", + "Data/Scripts/utility.pex", + "Data/Scripts/shout.pex", + "Data/Scripts/input.pex", + "Data/Scripts/race.pex", + "Data/Scripts/sounddescriptor.pex", + "Data/Scripts/wornobject.pex", + "Data/Scripts/ammo.pex", + "Data/Scripts/defaultobjectmanager.pex", + "Data/Scripts/camera.pex", + "Data/Scripts/apparatus.pex", + "skse64_1_6_1170.dll", + "Data/Scripts/magiceffect.pex", + "Data/Scripts/location.pex", + "Data/Scripts/alias.pex", + "Data/Scripts/treeobject.pex", + "Data/Scripts/leveledactor.pex", + "Data/Scripts/enchantment.pex", + "Data/Scripts/uicallback.pex", + "Data/Scripts/flora.pex", + "Data/Scripts/outfit.pex", + "Data/Scripts/cell.pex", ] priority = 0 [[mods]] -id = "deadly_spells" +id = "SkyUI-12604-35407" files = [ - [ - "000 Core Files/textures/impactdecals/decalsnowhole01_n.dds", - "Data/textures/impactdecals/decalsnowhole01_n.dds", -], - [ - "40 Two Fire Esp/textures/impactdecals/decalflamespread01_g.dds", - "Data/textures/impactdecals/decalflamespread01_g.dds", -], - [ - "000 Core Files/textures/impactdecals/decalsparkburn01_g.dds", - "Data/textures/impactdecals/decalsparkburn01_g.dds", -], - [ - "40 Two Fire Esp/textures/impactdecals/decalflamespread01.dds", - "Data/textures/impactdecals/decalflamespread01.dds", -], - [ - "000 Core Files/textures/impactdecals/decalfrostimpact01_n.dds", - "Data/textures/impactdecals/decalfrostimpact01_n.dds", -], - [ - "000 Core Files/textures/impactdecals/decalspitimpact01_n.dds", - "Data/textures/impactdecals/decalspitimpact01_n.dds", -], - [ - "40 Two Fire Esp/DeadlySpellImpacts - Two Fire.esp", - "Data/DeadlySpellImpacts - Two Fire.esp", -], - [ - "000 Core Files/textures/impactdecals/decalsnowmelt01.dds", - "Data/textures/impactdecals/decalsnowmelt01.dds", -], - [ - "000 Core Files/textures/impactdecals/decalspitimpact01.dds", - "Data/textures/impactdecals/decalspitimpact01.dds", -], - [ - "000 Core Files/textures/impactdecals/decalsnowhole01.dds", - "Data/textures/impactdecals/decalsnowhole01.dds", -], - [ - "000 Core Files/textures/impactdecals/decalsparkburn01.dds", - "Data/textures/impactdecals/decalsparkburn01.dds", -], - [ - "10 Fire Cracks/textures/impactdecals/decalflameburn01_g.dds", - "Data/textures/impactdecals/decalflameburn01_g.dds", -], - [ - "000 Core Files/textures/impactdecals/decalsnowmelt01_n.dds", - "Data/textures/impactdecals/decalsnowmelt01_n.dds", -], - [ - "000 Core Files/textures/impactdecals/decalfrostimpact01.dds", - "Data/textures/impactdecals/decalfrostimpact01.dds", -], - [ - "10 Fire Cracks/textures/impactdecals/decalflameburn01_n.dds", - "Data/textures/impactdecals/decalflameburn01_n.dds", -], - [ - "000 Core Files/textures/impactdecals/decalsnowmelt01_g.dds", - "Data/textures/impactdecals/decalsnowmelt01_g.dds", -], - [ - "000 Core Files/textures/impactdecals/decalsparkburn01_n.dds", - "Data/textures/impactdecals/decalsparkburn01_n.dds", -], - [ - "000 Core Files/textures/impactdecals/decalsnowhole01_g.dds", - "Data/textures/impactdecals/decalsnowhole01_g.dds", -], - [ - "40 Two Fire Esp/Manual Installation of the Two Fire Option.txt", - "Data/Manual Installation of the Two Fire Option.txt", -], - [ - "10 Fire Cracks/textures/impactdecals/decalflameburn01.dds", - "Data/textures/impactdecals/decalflameburn01.dds", -], - [ - "40 Two Fire Esp/textures/impactdecals/decalflamespread01_n.dds", - "Data/textures/impactdecals/decalflamespread01_n.dds", -], - [ - "000 Core Files/DeadlySpellImpacts.esp", - "Data/DeadlySpellImpacts.esp", -], + "SkyUI_SE.bsa -> Data/SkyUI_SE.bsa", + "SkyUI_SE.esp -> Data/SkyUI_SE.esp", ] -priority = 1 +priority = 0 + +[[mods]] +id = "racemenu-19080-465102" +files = [ + "RaceMenu.esp -> Data/RaceMenu.esp", + "SKSE/Plugins/skee64.ini -> Data/SKSE/Plugins/skee64.ini", + "RaceMenu.bsa -> Data/RaceMenu.bsa", + "RaceMenuPlugin.esp -> Data/RaceMenuPlugin.esp", + "SKSE/Plugins/skee64.dll -> Data/SKSE/Plugins/skee64.dll", +] +priority = 0 + diff --git a/tests/modded_instance_test.rs b/tests/modded_instance_test.rs index 0d3491c..7926dd2 100644 --- a/tests/modded_instance_test.rs +++ b/tests/modded_instance_test.rs @@ -38,14 +38,14 @@ fn parse_complex() { let unwraped = inst.expect("Asserted before"); assert_eq!(unwraped.game_id(), "sse"); - assert_eq!(unwraped.load_order().len(), 11); + assert_eq!(unwraped.load_order().len(), 13); assert_eq!( unwraped.game_file_overrides().first().unwrap(), &Link::new("skse64_loader.exe", "SkyrimSELauncher.exe") ); assert_eq!(unwraped.mods().len(), 3); - let test_mod = unwraped.mods().iter().find(|e| e.mod_id() == "skyui"); + let test_mod = unwraped.mods().iter().find(|e| e.mod_id() == "SkyUI-12604-35407"); assert!(test_mod.is_some()); assert_eq!(test_mod.unwrap().priority(), 0);