leyline/profile/
browser.rs1use crate::profile::{BrowserProfile, ChromiumBrand, Platform, PlatformIdentity};
2use crate::{Error, Kind};
3
4mod digest;
5
6pub(crate) use digest::digest;
7
8include!(concat!(env!("OUT_DIR"), "/browser.rs"));
9
10struct Entry {
11 family: Family,
12 key: &'static str,
13 version: u32,
14 id: &'static str,
15 name: &'static str,
16 hello: Browser,
17 platforms: &'static [(&'static str, Browser)],
18 digest: u64,
19 source: &'static str,
20}
21
22impl Family {
23 #[must_use]
24 pub fn all() -> &'static [Family] {
25 FAMILY_ALL
26 }
27
28 #[must_use]
29 pub fn id(&self) -> &'static str {
30 FAMILY_IDS[*self as usize]
31 }
32
33 pub(crate) fn samesite_checks_redirect_chain(self) -> bool {
34 FAMILY_SAMESITE_REDIRECT_CHAIN[self as usize]
35 }
36}
37
38impl std::fmt::Display for Family {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 f.write_str(FAMILY_LABELS[*self as usize])
41 }
42}
43
44impl std::str::FromStr for Family {
45 type Err = Error;
46
47 fn from_str(id: &str) -> Result<Self, Self::Err> {
48 FAMILY_ALL
49 .iter()
50 .copied()
51 .find(|family| family.id().eq_ignore_ascii_case(id))
52 .ok_or_else(|| unknown("family", id, FAMILY_IDS.iter().copied()))
53 }
54}
55
56string_id_serde!(Family);
57
58impl std::str::FromStr for Browser {
59 type Err = Error;
60
61 fn from_str(id: &str) -> Result<Self, Self::Err> {
62 ALL.iter()
63 .copied()
64 .find(|browser| browser.id().eq_ignore_ascii_case(id))
65 .ok_or_else(|| unknown("browser", id, ALL.iter().map(Browser::id)))
66 }
67}
68
69string_id_serde!(Browser);
70
71pub(crate) fn unknown<'a>(kind: &str, id: &str, known: impl Iterator<Item = &'a str>) -> Error {
72 Error::new(Kind::Config).with_message(format!(
73 "unknown {kind} {id:?}; expected one of {}",
74 known.collect::<Vec<_>>().join(", ")
75 ))
76}
77
78impl Browser {
79 pub fn all() -> &'static [Browser] {
80 ALL
81 }
82
83 #[must_use]
84 pub fn id(&self) -> &'static str {
85 self.entry().id
86 }
87
88 pub(crate) fn profile_digest(self) -> u64 {
89 self.entry().digest
90 }
91
92 fn entry(self) -> &'static Entry {
93 &ENTRIES[self as usize]
94 }
95
96 pub(crate) fn profile_key(&self) -> (&'static str, u32) {
97 let entry = self.entry();
98 (entry.key, entry.version)
99 }
100
101 #[must_use]
102 pub fn get(family: Family, version: u32) -> Option<Self> {
103 ALL.iter().copied().find(|browser| {
104 let entry = browser.entry();
105 entry.family == family && entry.version == version
106 })
107 }
108
109 #[must_use]
110 pub fn latest(family: Family) -> Self {
111 FAMILY_LATEST[family as usize]
112 }
113
114 #[must_use]
115 pub fn family(&self) -> Family {
116 self.entry().family
117 }
118
119 #[must_use]
120 pub fn version(&self) -> u32 {
121 self.entry().version
122 }
123
124 #[must_use]
125 pub(crate) fn hello_rep(self) -> Self {
126 self.entry().hello
127 }
128
129 #[must_use]
130 pub fn for_platform(self, platform: Platform) -> Self {
131 let key = platform.resolve().identity_key();
132 self.entry()
133 .platforms
134 .iter()
135 .find(|(name, _)| *name == key)
136 .map_or(self, |&(_, browser)| browser)
137 }
138
139 #[must_use]
140 pub fn identity(
141 self,
142 platform: Platform,
143 brand: Option<ChromiumBrand>,
144 ) -> Option<PlatformIdentity> {
145 self.platform_profile(platform)
146 .resolve_identity(platform, brand.unwrap_or_default())
147 .ok()
148 }
149
150 pub(crate) fn platform_profile(self, platform: Platform) -> &'static BrowserProfile {
151 self.for_platform(platform).profile()
152 }
153
154 pub(crate) fn profile_source(self) -> &'static str {
155 self.entry().source
156 }
157
158 pub(crate) fn matching_source(text: &str) -> Option<Self> {
159 let wanted = digest(&[text.as_bytes()]);
160 ALL.iter()
161 .copied()
162 .find(|browser| digest(&[browser.profile_source().as_bytes()]) == wanted)
163 }
164
165 pub(crate) fn default_browser() -> Self {
166 Self::latest(DEFAULT_FAMILY)
167 }
168
169 #[cfg(feature = "bench-internals")]
170 pub fn chromium_major(&self) -> Option<u32> {
171 self.profile().meta.chromium_major
172 }
173}
174
175impl Default for Browser {
176 fn default() -> Self {
177 Self::default_browser()
178 }
179}
180
181impl std::fmt::Display for Browser {
182 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183 f.write_str(self.entry().name)
184 }
185}
186
187#[cfg(test)]
188mod tests;