Skip to main content

modde_games/witcher3/
scanner.rs

1//! Mod scanner for The Witcher 3.
2
3use anyhow::Result;
4
5use crate::scanner_patterns::DirectoryModRule;
6use crate::traits::{DiscoveredMod, ModScanner, ScanContext};
7
8/// [`ModScanner`] that discovers installed Witcher 3 mods.
9pub struct Witcher3Scanner;
10
11pub static WITCHER3_SCANNER: Witcher3Scanner = Witcher3Scanner;
12
13impl ModScanner for Witcher3Scanner {
14    fn scan_directories(&self) -> &[&str] {
15        &["mods", "dlc"]
16    }
17
18    fn scan_filesystem(&self, ctx: &ScanContext<'_>) -> Result<Vec<DiscoveredMod>> {
19        let mut out = Vec::new();
20        DirectoryModRule {
21            rel_dir: "mods",
22            mod_id_prefix: "mod",
23            source_location: "mods",
24            confidence: 0.9,
25            marker_file: None,
26            marker_confidence: None,
27        }
28        .scan(ctx.install_dir, &mut out)?;
29        DirectoryModRule {
30            rel_dir: "dlc",
31            mod_id_prefix: "dlc",
32            source_location: "dlc",
33            confidence: 0.75,
34            marker_file: None,
35            marker_confidence: None,
36        }
37        .scan(ctx.install_dir, &mut out)?;
38        Ok(out)
39    }
40
41    fn mod_id_footprint(&self, mod_id: &str) -> Option<modde_core::scanner::ModFootprint> {
42        if let Some(name) = mod_id.strip_prefix("mod/") {
43            return Some(modde_core::scanner::ModFootprint::Directory(format!(
44                "mods/{}/",
45                name.to_lowercase()
46            )));
47        }
48        let name = mod_id.strip_prefix("dlc/")?.to_lowercase();
49        Some(modde_core::scanner::ModFootprint::Directory(format!(
50            "dlc/{name}/"
51        )))
52    }
53}