1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use std::{
    convert::Infallible,
    fmt::Display,
    path::{Path, PathBuf},
    process::ExitStatus,
    str::FromStr,
};

use crate::{
    error::{Error, Kind},
    pass, throw, Proton,
};

#[derive(Debug)]
pub struct Runtime {
    version: RunTimeVersion,
    path: PathBuf,
    proton: Proton,
}

impl Runtime {
    pub fn from_proton(version: RunTimeVersion, proton: Proton) -> Result<Self, Error> {
        Ok(Self {
            version,
            path: Self::find(&proton.common, version)?,
            proton,
        })
    }

    pub fn execute(self) -> Result<ExitStatus, Error> {
        use std::process::{Child, Command};

        let envs: Vec<(String, String)> = self.proton.gen_options();

        let mut child: Child = match Command::new(&self.path)
        .arg(&self.proton.path)
        .arg("runinprefix")
        .arg(&self.proton.program)
        .args(&self.proton.args)
        .env("STEAM_COMPAT_DATA_PATH", &self.proton.compat)
        .env("STEAM_COMPAT_CLIENT_INSTALL_PATH", &self.proton.steam)
        .envs(envs)
        .spawn() {
            Ok(child) => child,
            Err(e) => throw!(Kind::ProtonExit, "{}", e),
        };


        let status: ExitStatus = match child.wait() {
            Ok(e) => e,
            Err(e) => throw!(Kind::ProtonWait, "'{}': {}", child.id(), e),
        };

        pass!(status)
    }

    pub fn find(common: &Path, version: RunTimeVersion) -> Result<PathBuf, Error> {
        let tmp = format!("{}/{}/run", common.display(), version);
        let path = PathBuf::from(tmp);

        if path.exists() {
            pass!(path)
        } else {
            throw!(Kind::RuntimeMissing, "{}", version)
        }
    }
}

/// Enum to represet Steam runtime versions
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum RunTimeVersion {
    /// Default version of Steam's runtime
    Default,
    /// Sniper version of Steam's runtime
    Sniper,
    /// Soldier version of Steam's runtime
    Soldier,
    /// BattleEye version of Steam's runtime
    BattleEye,
}

impl Display for RunTimeVersion {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            RunTimeVersion::Default => write!(f, "SteamLinuxRuntime"),
            RunTimeVersion::Sniper => write!(f, "SteamLinuxRuntime_sniper"),
            RunTimeVersion::Soldier => write!(f, "SteamLinuxRuntime_soldier"),
            RunTimeVersion::BattleEye => write!(f, "Proton BattlEye Runtime"),
        }
    }
}

impl FromStr for RunTimeVersion {
    type Err = Infallible;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(match s {
            "default" => Self::Default,
            "soldier" => Self::Soldier,
            "sniper" => Self::Sniper,
            "battleeye" => Self::BattleEye,
            _ => Self::Default,
        })
    }
}