1#[macro_use]
2mod macros;
3pub mod compress;
4pub mod profile;
5
6use profile::{chrome::*, firefox::*, okhttp::*, opera::*, safari::*};
7#[cfg(feature = "emulation-serde")]
8use serde::{Deserialize, Serialize};
9use typed_builder::TypedBuilder;
10
11define_enum!(
12 dispatch,
18 Profile, Chrome100,
19 Emulation,
20
21 Chrome100 => ("chrome_100", v100::emulation),
23 Chrome101 => ("chrome_101", v101::emulation),
24 Chrome104 => ("chrome_104", v104::emulation),
25 Chrome105 => ("chrome_105", v105::emulation),
26 Chrome106 => ("chrome_106", v106::emulation),
27 Chrome107 => ("chrome_107", v107::emulation),
28 Chrome108 => ("chrome_108", v108::emulation),
29 Chrome109 => ("chrome_109", v109::emulation),
30 Chrome110 => ("chrome_110", v110::emulation),
31 Chrome114 => ("chrome_114", v114::emulation),
32 Chrome116 => ("chrome_116", v116::emulation),
33 Chrome117 => ("chrome_117", v117::emulation),
34 Chrome118 => ("chrome_118", v118::emulation),
35 Chrome119 => ("chrome_119", v119::emulation),
36 Chrome120 => ("chrome_120", v120::emulation),
37 Chrome123 => ("chrome_123", v123::emulation),
38 Chrome124 => ("chrome_124", v124::emulation),
39 Chrome126 => ("chrome_126", v126::emulation),
40 Chrome127 => ("chrome_127", v127::emulation),
41 Chrome128 => ("chrome_128", v128::emulation),
42 Chrome129 => ("chrome_129", v129::emulation),
43 Chrome130 => ("chrome_130", v130::emulation),
44 Chrome131 => ("chrome_131", v131::emulation),
45 Chrome132 => ("chrome_132", v132::emulation),
46 Chrome133 => ("chrome_133", v133::emulation),
47 Chrome134 => ("chrome_134", v134::emulation),
48 Chrome135 => ("chrome_135", v135::emulation),
49 Chrome136 => ("chrome_136", v136::emulation),
50 Chrome137 => ("chrome_137", v137::emulation),
51 Chrome138 => ("chrome_138", v138::emulation),
52 Chrome139 => ("chrome_139", v139::emulation),
53 Chrome140 => ("chrome_140", v140::emulation),
54 Chrome141 => ("chrome_141", v141::emulation),
55 Chrome142 => ("chrome_142", v142::emulation),
56 Chrome143 => ("chrome_143", v143::emulation),
57 Chrome144 => ("chrome_144", v144::emulation),
58 Chrome145 => ("chrome_145", v145::emulation),
59 Chrome146 => ("chrome_146", v146::emulation),
60 Chrome147 => ("chrome_147", v147::emulation),
61 Chrome148 => ("chrome_148", v148::emulation),
62 Chrome149 => ("chrome_149", v149::emulation),
63
64 Edge101 => ("edge_101", edge101::emulation),
66 Edge122 => ("edge_122", edge122::emulation),
67 Edge127 => ("edge_127", edge127::emulation),
68 Edge131 => ("edge_131", edge131::emulation),
69 Edge134 => ("edge_134", edge134::emulation),
70 Edge135 => ("edge_135", edge135::emulation),
71 Edge136 => ("edge_136", edge136::emulation),
72 Edge137 => ("edge_137", edge137::emulation),
73 Edge138 => ("edge_138", edge138::emulation),
74 Edge139 => ("edge_139", edge139::emulation),
75 Edge140 => ("edge_140", edge140::emulation),
76 Edge141 => ("edge_141", edge141::emulation),
77 Edge142 => ("edge_142", edge142::emulation),
78 Edge143 => ("edge_143", edge143::emulation),
79 Edge144 => ("edge_144", edge144::emulation),
80 Edge145 => ("edge_145", edge145::emulation),
81 Edge146 => ("edge_146", edge146::emulation),
82 Edge147 => ("edge_147", edge147::emulation),
83 Edge148 => ("edge_148", edge148::emulation),
84
85 Opera116 => ("opera_116", opera116::emulation),
87 Opera117 => ("opera_117", opera117::emulation),
88 Opera118 => ("opera_118", opera118::emulation),
89 Opera119 => ("opera_119", opera119::emulation),
90 Opera120 => ("opera_120", opera120::emulation),
91 Opera121 => ("opera_121", opera121::emulation),
92 Opera122 => ("opera_122", opera122::emulation),
93 Opera123 => ("opera_123", opera123::emulation),
94 Opera124 => ("opera_124", opera124::emulation),
95 Opera125 => ("opera_125", opera125::emulation),
96 Opera126 => ("opera_126", opera126::emulation),
97 Opera127 => ("opera_127", opera127::emulation),
98 Opera128 => ("opera_128", opera128::emulation),
99 Opera129 => ("opera_129", opera129::emulation),
100 Opera130 => ("opera_130", opera130::emulation),
101 Opera131 => ("opera_131", opera131::emulation),
102
103 Firefox109 => ("firefox_109", ff109::emulation),
105 Firefox117 => ("firefox_117", ff117::emulation),
106 Firefox128 => ("firefox_128", ff128::emulation),
107 Firefox133 => ("firefox_133", ff133::emulation),
108 Firefox135 => ("firefox_135", ff135::emulation),
109 FirefoxPrivate135 => ("firefox_private_135", ff_private_135::emulation),
110 FirefoxAndroid135 => ("firefox_android_135", ff_android_135::emulation),
111 Firefox136 => ("firefox_136", ff136::emulation),
112 FirefoxPrivate136 => ("firefox_private_136", ff_private_136::emulation),
113 Firefox139 => ("firefox_139", ff139::emulation),
114 Firefox142 => ("firefox_142", ff142::emulation),
115 Firefox143 => ("firefox_143", ff143::emulation),
116 Firefox144 => ("firefox_144", ff144::emulation),
117 Firefox145 => ("firefox_145", ff145::emulation),
118 Firefox146 => ("firefox_146", ff146::emulation),
119 Firefox147 => ("firefox_147", ff147::emulation),
120 Firefox148 => ("firefox_148", ff148::emulation),
121 Firefox149 => ("firefox_149", ff149::emulation),
122 Firefox150 => ("firefox_150", ff150::emulation),
123 Firefox151 => ("firefox_151", ff151::emulation),
124
125 SafariIos17_2 => ("safari_ios_17.2", safari_ios_17_2::emulation),
127 SafariIos17_4_1 => ("safari_ios_17.4.1", safari_ios_17_4_1::emulation),
128 SafariIos16_5 => ("safari_ios_16.5", safari_ios_16_5::emulation),
129 Safari15_3 => ("safari_15.3", safari15_3::emulation),
130 Safari15_5 => ("safari_15.5", safari15_5::emulation),
131 Safari15_6_1 => ("safari_15.6.1", safari15_6_1::emulation),
132 Safari16 => ("safari_16", safari16::emulation),
133 Safari16_5 => ("safari_16.5", safari16_5::emulation),
134 Safari17_0 => ("safari_17.0", safari17_0::emulation),
135 Safari17_2_1 => ("safari_17.2.1", safari17_2_1::emulation),
136 Safari17_4_1 => ("safari_17.4.1", safari17_4_1::emulation),
137 Safari17_5 => ("safari_17.5", safari17_5::emulation),
138 Safari17_6 => ("safari_17.6", safari17_6::emulation),
139 Safari18 => ("safari_18", safari18::emulation),
140 SafariIPad18 => ("safari_ipad_18", safari_ipad_18::emulation),
141 Safari18_2 => ("safari_18.2", safari18_2::emulation),
142 SafariIos18_1_1 => ("safari_ios_18.1.1", safari_ios_18_1_1::emulation),
143 Safari18_3 => ("safari_18.3", safari18_3::emulation),
144 Safari18_3_1 => ("safari_18.3.1", safari18_3_1::emulation),
145 Safari18_5 => ("safari_18.5", safari18_5::emulation),
146 Safari26 => ("safari_26", safari26::emulation),
147 Safari26_1 => ("safari_26.1", safari26_1::emulation),
148 Safari26_2 => ("safari_26.2", safari26_2::emulation),
149 Safari26_3 => ("safari_26.3", safari26_3::emulation),
150 Safari26_4 => ("safari_26.4", safari26_4::emulation),
151 SafariIPad26 => ("safari_ipad_26", safari_ipad_26::emulation),
152 SafariIpad26_2 => ("safari_ipad_26.2", safari_ipad_26_2::emulation),
153 SafariIos26 => ("safari_ios_26", safari_ios_26::emulation),
154 SafariIos26_2 => ("safari_ios_26.2", safari_ios_26_2::emulation),
155
156 OkHttp3_9 => ("okhttp_3.9", okhttp3_9::emulation),
158 OkHttp3_11 => ("okhttp_3.11", okhttp3_11::emulation),
159 OkHttp3_13 => ("okhttp_3.13", okhttp3_13::emulation),
160 OkHttp3_14 => ("okhttp_3.14", okhttp3_14::emulation),
161 OkHttp4_9 => ("okhttp_4.9", okhttp4_9::emulation),
162 OkHttp4_10 => ("okhttp_4.10", okhttp4_10::emulation),
163 OkHttp4_12 => ("okhttp_4.12", okhttp4_12::emulation),
164 OkHttp5 => ("okhttp_5", okhttp5::emulation)
165
166);
167
168impl wreq::IntoEmulation for Profile {
169 #[inline]
170 fn into_emulation(self) -> wreq::Emulation {
171 Emulation::builder().profile(self).build().into_emulation()
172 }
173}
174
175define_enum!(
176 plain,
182 Platform, MacOS,
183 Windows => "windows",
184 MacOS => "macos",
185 Linux => "linux",
186 Android => "android",
187 IOS => "ios"
188);
189
190impl Platform {
191 #[inline]
192 const fn platform(&self) -> &'static str {
193 match self {
194 Platform::MacOS => "\"macOS\"",
195 Platform::Linux => "\"Linux\"",
196 Platform::Windows => "\"Windows\"",
197 Platform::Android => "\"Android\"",
198 Platform::IOS => "\"iOS\"",
199 }
200 }
201
202 #[inline]
203 const fn is_mobile(&self) -> bool {
204 matches!(self, Platform::Android | Platform::IOS)
205 }
206}
207
208#[derive(Default, Clone, TypedBuilder)]
214pub struct Emulation {
215 #[builder(default)]
217 profile: Profile,
218
219 #[builder(default)]
221 platform: Platform,
222
223 #[builder(default = true)]
225 http2: bool,
226
227 #[builder(default = true)]
229 headers: bool,
230}
231
232impl Emulation {
233 pub fn random() -> Emulation {
244 let rand = crate::rand::fast_random();
245 Emulation::builder()
246 .profile(Profile::VARIANTS[(rand as usize) % Profile::VARIANTS.len()])
247 .platform(Platform::VARIANTS[((rand >> 32) as usize) % Platform::VARIANTS.len()])
248 .build()
249 }
250
251 pub fn weighted_random() -> Emulation {
278 use Platform::*;
279 use Profile::*;
280
281 struct Class {
282 weight: u32,
283 platforms: &'static [Platform],
284 profiles: &'static [Profile],
285 }
286
287 const CLASSES: &[Class] = &[
299 Class {
300 weight: 7141,
301 platforms: &[Windows, MacOS, Linux, Android],
302 profiles: &[
303 Chrome149, Chrome148, Chrome147, Chrome146, Chrome145, Chrome144, Chrome143,
304 ],
305 },
306 Class {
307 weight: 1240,
308 platforms: &[IOS],
309 profiles: &[
310 SafariIos26_2,
311 SafariIos26,
312 SafariIpad26_2,
313 SafariIPad26,
314 SafariIos18_1_1,
315 SafariIPad18,
316 ],
317 },
318 Class {
319 weight: 502,
320 platforms: &[Windows, MacOS],
321 profiles: &[Edge148, Edge147, Edge146, Edge145, Edge144, Edge143],
322 },
323 Class {
324 weight: 237,
325 platforms: &[MacOS],
326 profiles: &[
327 Safari26_4, Safari26_3, Safari26_2, Safari26_1, Safari26, Safari18_5,
328 ],
329 },
330 Class {
331 weight: 235,
332 platforms: &[Windows, MacOS, Linux],
333 profiles: &[
334 Firefox151, Firefox150, Firefox149, Firefox148, Firefox147, Firefox146,
335 ],
336 },
337 Class {
338 weight: 173,
339 platforms: &[Windows, MacOS, Linux, Android],
340 profiles: &[Opera131, Opera130, Opera129, Opera128, Opera127, Opera126],
341 },
342 ];
343
344 let (r1, r2) = (crate::rand::fast_random(), crate::rand::fast_random());
345 let total: u32 = CLASSES.iter().map(|c| c.weight).sum();
346 let mut t = (r1 % total as u64) as u32;
347 let class = CLASSES
348 .iter()
349 .find(|c| {
350 t = t.checked_sub(c.weight).unwrap_or(u32::MAX);
351 t == u32::MAX
352 })
353 .unwrap_or(&CLASSES[0]);
354 let n = class.profiles.len();
355 let idx = ((r1 >> 32) as usize % n).min((r2 >> 32) as usize % n);
356 Emulation::builder()
357 .profile(class.profiles[idx])
358 .platform(class.platforms[(r2 as usize) % class.platforms.len()])
359 .build()
360 }
361}
362
363impl wreq::IntoEmulation for Emulation {
364 #[inline]
365 fn into_emulation(self) -> wreq::Emulation {
366 self.profile.match_emulation(self)
367 }
368}