Skip to main content

loadsmith_loader/
args.rs

1use std::{collections::HashMap, ffi::OsString, fmt::Debug, path::Path, path::PathBuf};
2
3/// Arguments, environment variables, and an optional wrapper binary to use
4/// when launching a game with a mod loader.
5///
6/// Build one using the builder methods ([`arg`](LaunchArgs::arg),
7/// [`env`](LaunchArgs::env), [`wrapper`](LaunchArgs::wrapper)), then call
8/// [`apply`](LaunchArgs::apply) to configure a
9/// [`std::process::Command`].
10///
11/// # Examples
12///
13/// ```rust
14/// use loadsmith_loader::LaunchArgs;
15/// use std::process::Command;
16///
17/// let args = LaunchArgs::new()
18///     .arg("--doorstop-enable")
19///     .arg("true")
20///     .env("DOORSTOP_ENABLE", "TRUE");
21///
22/// let mut cmd = Command::new("game.exe");
23/// args.apply(&mut cmd);
24///
25/// assert_eq!(cmd.get_args().collect::<Vec<_>>(), ["--doorstop-enable", "true"]);
26/// ```
27#[derive(Debug, Clone, PartialEq, Eq, Default)]
28pub struct LaunchArgs {
29    args: Vec<OsString>,
30    env: HashMap<OsString, OsString>,
31    wrapper: Option<PathBuf>,
32}
33
34impl LaunchArgs {
35    /// Creates an empty set of launch arguments.
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    /// Appends a positional argument.
41    pub fn arg(mut self, arg: impl Into<OsString>) -> Self {
42        self.args.push(arg.into());
43        self
44    }
45
46    /// Sets an environment variable.
47    pub fn env(mut self, key: impl Into<OsString>, value: impl Into<OsString>) -> Self {
48        self.env.insert(key.into(), value.into());
49        self
50    }
51
52    /// Sets a wrapper binary (e.g. `wine` or `proton`) that the command
53    /// should be launched through.
54    pub fn wrapper(mut self, wrapper: impl Into<PathBuf>) -> Self {
55        self.wrapper = Some(wrapper.into());
56        self
57    }
58
59    /// Returns the positional arguments.
60    pub fn get_args(&self) -> &[OsString] {
61        &self.args
62    }
63
64    /// Returns the environment variables.
65    pub fn get_env(&self) -> &HashMap<OsString, OsString> {
66        &self.env
67    }
68
69    /// Returns the wrapper binary path, if set.
70    pub fn get_wrapper(&self) -> Option<&Path> {
71        self.wrapper.as_deref()
72    }
73
74    /// Applies these arguments, environment, and optional wrapper to a
75    /// [`std::process::Command`].
76    ///
77    /// When a wrapper is set the original command's program and arguments
78    /// become sub-arguments of the wrapper.
79    pub fn apply(self, command: &mut std::process::Command) {
80        if let Some(wrapper) = self.wrapper {
81            let mut new_command = std::process::Command::new(wrapper);
82            new_command
83                .arg(command.get_program())
84                .args(command.get_args());
85
86            for (key, value) in command.get_envs() {
87                if let Some(value) = value {
88                    new_command.env(key, value);
89                } else {
90                    new_command.env_remove(key);
91                }
92            }
93
94            *command = new_command;
95        }
96
97        for arg in self.args {
98            command.arg(arg);
99        }
100
101        for (key, value) in self.env {
102            command.env(key, value);
103        }
104    }
105}