Skip to main content

loadsmith_loader/
doorstop.rs

1use std::fs;
2
3use tracing::{debug, warn};
4
5use crate::{Error, LaunchContext, Result};
6
7const FALLBACK_VERSION: u32 = 3;
8
9/// Returns the Doorstop argument flag names for the given major version.
10///
11/// When `version_override` is `Some`, that version is used directly.
12/// Otherwise the version is read from a `.doorstop_version` file in the
13/// profile directory. If that file is missing, version 3 is assumed.
14///
15/// Returns a tuple of `(enable_flag, target_flag)`.
16///
17/// # Errors
18///
19/// Returns [`Error::InvalidDoorstopVersionFormat`] when the version file
20/// cannot be parsed, or [`Error::UnsupportedDoorstopVersion`] for versions
21/// other than 3 or 4.
22///
23/// # Examples
24///
25/// ```rust
26/// use loadsmith_loader::LaunchContext;
27/// use loadsmith_loader::doorstop;
28/// use camino::Utf8Path;
29///
30/// let ctx = LaunchContext::new(
31///     Utf8Path::new("/tmp/profile"),
32///     Utf8Path::new("/tmp/game"),
33///     false,
34/// );
35///
36/// let (enable, target) = doorstop::args(Some(4), &ctx).unwrap();
37/// assert_eq!(enable, "--doorstop-enabled");
38/// assert_eq!(target, "--doorstop-target-assembly");
39/// ```
40pub fn args(
41    version_override: Option<u32>,
42    ctx: &LaunchContext,
43) -> Result<(&'static str, &'static str)> {
44    let version = if let Some(v) = version_override {
45        debug!("using doorstop version override: {}", v);
46        v
47    } else {
48        let path = ctx.profile_path().join(".doorstop_version");
49
50        if path.exists() {
51            let version_content = fs::read_to_string(&path)?;
52            let version = version_content
53                .split('.') // read only the major version number
54                .next()
55                .and_then(|str| str.parse().ok())
56                .ok_or(Error::InvalidDoorstopVersionFormat(version_content))?;
57
58            debug!(version, "doorstop version read");
59            version
60        } else {
61            warn!(
62                fallback = FALLBACK_VERSION,
63                ".doorstop_version file is missing"
64            );
65            FALLBACK_VERSION
66        }
67    };
68
69    match version {
70        3 => Ok(("--doorstop-enable", "--doorstop-target")),
71        4 => Ok(("--doorstop-enabled", "--doorstop-target-assembly")),
72        v => Err(Error::UnsupportedDoorstopVersion(v)),
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn args_default() {
82        let tempdir = tempfile::tempdir().unwrap();
83        let ctx = LaunchContext::in_tempdir(&tempdir, false).unwrap();
84
85        let (enable, target) = args(None, &ctx).unwrap();
86        assert_eq!(enable, "--doorstop-enable");
87        assert_eq!(target, "--doorstop-target");
88    }
89
90    #[test]
91    fn args_version_override() {
92        let tempdir = tempfile::tempdir().unwrap();
93        let ctx = LaunchContext::in_tempdir(&tempdir, false).unwrap();
94
95        let (enable, target) = args(Some(4), &ctx).unwrap();
96        assert_eq!(enable, "--doorstop-enabled");
97        assert_eq!(target, "--doorstop-target-assembly");
98    }
99
100    #[test]
101    fn args_invalid_version_override() {
102        let tempdir = tempfile::tempdir().unwrap();
103        let ctx = LaunchContext::in_tempdir(&tempdir, false).unwrap();
104
105        let err = args(Some(5), &ctx).unwrap_err();
106        assert!(matches!(err, Error::UnsupportedDoorstopVersion(5)));
107    }
108
109    #[test]
110    fn args_version_file() {
111        let tempdir = tempfile::tempdir().unwrap();
112        let ctx = LaunchContext::in_tempdir(&tempdir, false).unwrap();
113
114        let version_path = ctx.profile_path().join(".doorstop_version");
115        std::fs::write(&version_path, "4.0.0").unwrap();
116
117        let (enable, target) = args(None, &ctx).unwrap();
118        assert_eq!(enable, "--doorstop-enabled");
119        assert_eq!(target, "--doorstop-target-assembly");
120    }
121
122    #[test]
123    fn args_invalid_version_file() {
124        let tempdir = tempfile::tempdir().unwrap();
125        let ctx = LaunchContext::in_tempdir(&tempdir, false).unwrap();
126
127        let version_path = ctx.profile_path().join(".doorstop_version");
128        std::fs::write(&version_path, "invalid").unwrap();
129
130        let err = args(None, &ctx).unwrap_err();
131        assert!(matches!(err, Error::InvalidDoorstopVersionFormat(_)));
132    }
133}