proton_launch/command/
run.rs1use std::{path::PathBuf, str::FromStr};
2
3use crate::{paths::Paths, proton::ProtonVersion, steam::SteamData};
4
5use super::{Runnable, RunnableError, RunnableResult};
6
7#[derive(Debug, Clone)]
8#[cfg_attr(feature = "commandline", derive(clap::Args))]
9pub struct Run {
10 exe: Option<PathBuf>,
13
14 #[cfg_attr(feature = "commandline", clap(last = true))]
18 args: Vec<String>,
19
20 #[cfg_attr(feature = "commandline", clap(short, long))]
23 save_name: Option<String>,
24
25 #[cfg_attr(feature = "commandline", clap(short, long))]
27 proton: Option<ProtonVersion>,
28
29 #[cfg_attr(feature = "commandline", clap(long))]
34 here: bool,
35}
36
37impl Run {
38 fn get_exe_and_args(&self) -> Result<(PathBuf, &[String]), RunnableError> {
39 if let Some(exe) = &self.exe {
40 Ok((exe.clone(), &self.args))
41 } else {
42 if let Some((exe, args)) = self.args.split_first() {
43 Ok((PathBuf::from_str(exe.as_str()).unwrap(), args))
44 } else {
45 Err(RunnableError::NoExe)
46 }
47 }
48 }
49}
50
51impl Runnable for Run {
52 fn run(&self, paths: &Paths, steam_data: &SteamData) -> RunnableResult<()> {
53 let selected_proton = self.proton.filter(|p| p.is_installed(steam_data));
54 if let Some(handpicked) = self.proton {
55 if selected_proton.is_none() {
56 return Err(RunnableError::SelectedProtonNotInstalled(handpicked));
57 }
58 }
59 let selected_proton = selected_proton.or_else(|| ProtonVersion::best_installed(steam_data));
60
61 let (exe, args) = self.get_exe_and_args()?;
62
63 if let Some(selected) = selected_proton {
64 let save_name = self
65 .save_name
66 .as_deref()
67 .unwrap_or_else(|| exe.file_stem().unwrap().to_str().unwrap());
68 let proton_path = selected.get_path(steam_data).expect("You somehow managed to delete the selected proton version while running this command");
69 let proton_command = proton_path.join("proton");
70
71 println!("Launching {} with {}", exe.display(), selected);
72
73 let compat_dir = paths.compat_dir(save_name);
74 let run_dir = if self.here {
75 exe.parent().unwrap().to_path_buf()
76 } else {
77 paths.run_dir(save_name)
78 };
79
80 let mut command = std::process::Command::new(proton_command);
81 command.env("STEAM_COMPAT_CLIENT_INSTALL_PATH", &steam_data.path);
82 command.env("STEAM_COMPAT_DATA_PATH", compat_dir);
83 command.current_dir(run_dir);
84 command.arg("run");
85 command.arg(exe);
86 command.args(args);
87
88 let res = command.spawn().map_err(RunnableError::SpawnError)?.wait()?;
89 println!("Exited with status {}", res);
90 Ok(())
91 } else {
92 Err(RunnableError::NoProtonAtAll)
93 }
94 }
95}