Skip to main content

opys_mojang_rules/
os.rs

1use regex::Regex;
2use serde::{Deserialize, Serialize};
3use thiserror::Error;
4
5/// Platform context passed to rule evaluation. Free-form strings — these are
6/// live OS detection values, not constrained by the wire schema.
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub struct OsOptions {
9    pub name: String,
10    pub version: String,
11    pub arch: String,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "lowercase")]
16pub enum OsName {
17    Linux,
18    Windows,
19    Osx,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23pub enum OsArch {
24    #[serde(rename = "x86")]
25    X86,
26    #[serde(rename = "x86_64")]
27    X86_64,
28    #[serde(rename = "arm")]
29    Arm,
30    #[serde(rename = "aarch64")]
31    Aarch64,
32    #[serde(rename = "any")]
33    Any,
34}
35
36impl OsName {
37    fn as_str(self) -> &'static str {
38        match self {
39            OsName::Linux => "linux",
40            OsName::Windows => "windows",
41            OsName::Osx => "osx",
42        }
43    }
44}
45
46impl OsArch {
47    fn as_str(self) -> &'static str {
48        match self {
49            OsArch::X86 => "x86",
50            OsArch::X86_64 => "x86_64",
51            OsArch::Arm => "arm",
52            OsArch::Aarch64 => "aarch64",
53            OsArch::Any => "any",
54        }
55    }
56}
57
58/// Constraint on OS as it appears in rule JSON. Every field is optional and
59/// independent — present must match, absent is ignored.
60#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
61pub struct OsConstraint {
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub name: Option<OsName>,
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub version: Option<String>,
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub arch: Option<OsArch>,
68}
69
70#[derive(Debug, Error)]
71pub enum RuleError {
72    #[error("Invalid OS version pattern \"{pattern}\": {source}")]
73    InvalidVersionRegex {
74        pattern: String,
75        #[source]
76        source: regex::Error,
77    },
78}
79
80pub fn satisfies_os(constraint: &OsConstraint, os: &OsOptions) -> Result<bool, RuleError> {
81    if let Some(name) = constraint.name {
82        if name.as_str() != os.name {
83            return Ok(false);
84        }
85    }
86    if let Some(arch) = constraint.arch {
87        if arch.as_str() != os.arch {
88            return Ok(false);
89        }
90    }
91    if let Some(pattern) = constraint.version.as_deref() {
92        let re = Regex::new(pattern).map_err(|source| RuleError::InvalidVersionRegex {
93            pattern: pattern.to_owned(),
94            source,
95        })?;
96        if !re.is_match(&os.version) {
97            return Ok(false);
98        }
99    }
100    Ok(true)
101}