1use regex::Regex;
2use serde::{Deserialize, Serialize};
3use thiserror::Error;
4
5#[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#[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}