Skip to main content

leyline/profile/
platform.rs

1use std::collections::HashMap;
2use std::sync::LazyLock;
3
4use serde::Deserialize;
5
6use crate::tcp::TcpProfile;
7
8#[derive(Deserialize)]
9struct PlatformData {
10    tcp: TcpProfile,
11}
12
13static PLATFORMS: LazyLock<HashMap<String, PlatformData>> = LazyLock::new(|| {
14    toml::from_str(include_str!("../../profiles/platforms.toml"))
15        .expect("built-in platform table is statically valid")
16});
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19#[non_exhaustive]
20#[derive(Default)]
21pub enum Platform {
22    #[default]
23    Windows,
24    MacOS,
25    Linux,
26    Android,
27    IOS,
28    Host,
29}
30
31const PLATFORM_ALL: &[Platform] = &[
32    Platform::Windows,
33    Platform::MacOS,
34    Platform::Linux,
35    Platform::Android,
36    Platform::IOS,
37    Platform::Host,
38];
39
40impl Platform {
41    #[must_use]
42    pub fn all() -> &'static [Platform] {
43        PLATFORM_ALL
44    }
45
46    #[must_use]
47    pub fn id(&self) -> &'static str {
48        match self {
49            Self::Host => "host",
50            other => other.identity_key(),
51        }
52    }
53
54    pub(crate) fn detect_host() -> Self {
55        #[cfg(target_os = "windows")]
56        {
57            Self::Windows
58        }
59        #[cfg(target_os = "macos")]
60        {
61            Self::MacOS
62        }
63        #[cfg(target_os = "linux")]
64        {
65            Self::Linux
66        }
67        #[cfg(target_os = "android")]
68        {
69            Self::Android
70        }
71        #[cfg(target_os = "ios")]
72        {
73            Self::IOS
74        }
75        #[cfg(not(any(
76            target_os = "windows",
77            target_os = "macos",
78            target_os = "linux",
79            target_os = "android",
80            target_os = "ios"
81        )))]
82        {
83            Self::Windows
84        }
85    }
86
87    #[must_use]
88    pub(crate) fn resolve(self) -> Self {
89        match self {
90            Self::Host => Self::detect_host(),
91            other => other,
92        }
93    }
94
95    pub(crate) fn sec_ch_platform(&self) -> &'static str {
96        match self {
97            Self::Windows => "Windows",
98            Self::MacOS => "macOS",
99            Self::Linux => "Linux",
100            Self::Android => "Android",
101            Self::IOS => "iOS",
102            Self::Host => Self::detect_host().sec_ch_platform(),
103        }
104    }
105
106    pub(crate) fn mobile_flag(&self) -> &'static str {
107        match self {
108            Self::Android | Self::IOS => "?1",
109            Self::Host => Self::detect_host().mobile_flag(),
110            _ => "?0",
111        }
112    }
113
114    #[must_use]
115    pub fn tcp_profile(&self) -> TcpProfile {
116        PLATFORMS
117            .get(self.identity_key())
118            .map(|data| data.tcp.clone())
119            .unwrap_or_default()
120    }
121
122    bench_pub! {
123        fn identity_key(&self) -> &'static str {
124            match self {
125                Self::Windows => "windows",
126                Self::MacOS => "macos",
127                Self::Linux => "linux",
128                Self::Android => "android",
129                Self::IOS => "ios",
130                Self::Host => Self::detect_host().identity_key(),
131            }
132        }
133    }
134}
135
136impl std::fmt::Display for Platform {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        write!(f, "{}", self.sec_ch_platform())
139    }
140}
141
142impl std::str::FromStr for Platform {
143    type Err = crate::Error;
144
145    fn from_str(id: &str) -> Result<Self, Self::Err> {
146        PLATFORM_ALL
147            .iter()
148            .copied()
149            .find(|platform| platform.id().eq_ignore_ascii_case(id))
150            .ok_or_else(|| {
151                crate::profile::browser::unknown(
152                    "platform",
153                    id,
154                    PLATFORM_ALL.iter().map(Platform::id),
155                )
156            })
157    }
158}
159
160string_id_serde!(Platform);