leyline/profile/
platform.rs1use 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);