Skip to main content

rustenium_identity/
preset.rs

1//! Built-in identity presets.
2//!
3//! Provides 10 realistic browser/device identities that can be retrieved by
4//! their numeric ID or selected at random, without any external dependency.
5//!
6//! # Example
7//! ```rust,no_run
8//! use rustenium_identity::preset;
9//!
10//! let id = preset::get_by_id(1).unwrap();
11//! let rnd = preset::random();
12//! println!("{:?}", rnd.browser);
13//! ```
14
15use crate::error::IdentityError;
16use crate::identity::{Browser, Gpu, Identity, IdentityCountryGeo, NavigatorPlatform, Os, PlatformInfo, ScreenResolution};
17
18// ---------------------------------------------------------------------------
19// Internal helpers
20// ---------------------------------------------------------------------------
21
22fn win_platform(ver: &str) -> PlatformInfo {
23    PlatformInfo {
24        bitness: Some("64".into()),
25        architecture: Some("x86".into()),
26        navigator_platform: NavigatorPlatform::Win32,
27        version: ver.into(),
28    }
29}
30
31fn mac_platform(ver: &str) -> PlatformInfo {
32    PlatformInfo {
33        bitness: Some("64".into()),
34        architecture: Some("arm".into()),
35        navigator_platform: NavigatorPlatform::MacIntel,
36        version: ver.into(),
37    }
38}
39
40fn lin_platform(ver: &str) -> PlatformInfo {
41    PlatformInfo {
42        bitness: Some("64".into()),
43        architecture: Some("x86".into()),
44        navigator_platform: NavigatorPlatform::LinuxX86_64,
45        version: ver.into(),
46    }
47}
48
49fn screen(lw: u16, lh: u16, ow: u16, oh: u16, dpr: f32) -> ScreenResolution {
50    ScreenResolution {
51        logical_width: lw,
52        logical_height: lh,
53        original_width: ow,
54        original_height: oh,
55        density_pixel_ratio: dpr,
56    }
57}
58
59fn nvidia_gpu(model: &str) -> Gpu {
60    Gpu {
61        vendor: "NVIDIA Corporation".into(),
62        webgl_renderer: format!("ANGLE (NVIDIA {} Direct3D11 vs_5_0 ps_5_0)", model),
63        webgl_vendor: "Google Inc. (NVIDIA)".into(),
64    }
65}
66
67fn intel_gpu(model: &str) -> Gpu {
68    Gpu {
69        vendor: "Intel Inc.".into(),
70        webgl_renderer: format!("ANGLE (Intel, {} Direct3D11 vs_5_0 ps_5_0)", model),
71        webgl_vendor: "Google Inc. (Intel)".into(),
72    }
73}
74
75fn apple_gpu(model: &str) -> Gpu {
76    Gpu {
77        vendor: "Apple".into(),
78        webgl_renderer: model.into(),
79        webgl_vendor: "Apple".into(),
80    }
81}
82
83fn amd_gpu(model: &str) -> Gpu {
84    Gpu {
85        vendor: "Advanced Micro Devices, Inc.".into(),
86        webgl_renderer: format!("ANGLE (AMD, {} Direct3D11 vs_5_0 ps_5_0)", model),
87        webgl_vendor: "Google Inc. (AMD)".into(),
88    }
89}
90
91// ---------------------------------------------------------------------------
92// Preset table (IDs 1-10)
93// ---------------------------------------------------------------------------
94
95fn all() -> [Identity; 10] {
96    [
97        // 1 — Windows 11 / Chrome / Desktop / NVIDIA RTX 3080
98        Identity {
99            id: Some(1),
100            device_model: None,
101            has_battery: false,
102            has_mouse: true,
103            has_touch: false,
104            os: Os::Windows,
105            os_version: "15.0.0".into(),
106            platform: win_platform("15.0.0"),
107            browser: Browser::Chrome,
108            browser_version: vec![124, 0, 6367, 78],
109            screen: screen(1920, 1080, 1920, 1080, 1.0),
110            hardware_concurrency: 16,
111            memory: 16,
112            gpu: nvidia_gpu("GeForce RTX 3080"),
113            geo: IdentityCountryGeo::US,
114            language: vec!["en-US".into(), "en".into()],
115            history_count: Some(12),
116            proxy: None,
117            timezone: None
118        },
119        // 2 — Windows 10 / Edge / Desktop / Intel UHD 630
120        Identity {
121            id: Some(2),
122            device_model: None,
123            has_battery: false,
124            has_mouse: true,
125            has_touch: false,
126            os: Os::Windows,
127            os_version: "10.0.0".into(),
128            platform: win_platform("10.0.0"),
129            browser: Browser::Edge,
130            browser_version: vec![123, 0, 2420, 65],
131            screen: screen(1366, 768, 1366, 768, 1.0),
132            hardware_concurrency: 8,
133            memory: 8,
134            gpu: intel_gpu("Intel(R) UHD Graphics 630"),
135            geo: IdentityCountryGeo::US,
136            language: vec!["en-US".into(), "en".into()],
137            history_count: Some(5),
138            proxy: None,
139            timezone: None
140        },
141        // 3 — macOS Ventura / Safari / Desktop / Apple M2
142        Identity {
143            id: Some(3),
144            device_model: None,
145            has_battery: true,
146            has_mouse: true,
147            has_touch: false,
148            os: Os::Macintosh,
149            os_version: "13.4.1".into(),
150            platform: mac_platform("13.4.1"),
151            browser: Browser::Safari,
152            browser_version: vec![17, 0, 0, 0],
153            screen: screen(2560, 1600, 2560, 1600, 2.0),
154            hardware_concurrency: 10,
155            memory: 16,
156            gpu: apple_gpu("Apple M2"),
157            geo: IdentityCountryGeo::UK,
158            language: vec!["en-GB".into(), "en".into()],
159            history_count: Some(20),
160            proxy: None,
161            timezone: None
162        },
163        // 4 — macOS Monterey / Chrome / Desktop / Apple M1
164        Identity {
165            id: Some(4),
166            device_model: None,
167            has_battery: true,
168            has_mouse: true,
169            has_touch: false,
170            os: Os::Macintosh,
171            os_version: "12.6.0".into(),
172            platform: mac_platform("12.6.0"),
173            browser: Browser::Chrome,
174            browser_version: vec![124, 0, 6367, 82],
175            screen: screen(1440, 900, 2880, 1800, 2.0),
176            hardware_concurrency: 8,
177            memory: 8,
178            gpu: apple_gpu("Apple M1"),
179            geo: IdentityCountryGeo::US,
180            language: vec!["en-US".into(), "en".into()],
181            history_count: Some(7),
182            proxy: None,
183            timezone: None
184        },
185        // 5 — Ubuntu Linux / Chrome / Desktop / NVIDIA GTX 1650
186        Identity {
187            id: Some(5),
188            device_model: None,
189            has_battery: false,
190            has_mouse: true,
191            has_touch: false,
192            os: Os::Linux,
193            os_version: "22.04".into(),
194            platform: lin_platform("22.04"),
195            browser: Browser::Chrome,
196            browser_version: vec![124, 0, 6367, 79],
197            screen: screen(1920, 1080, 1920, 1080, 1.0),
198            hardware_concurrency: 12,
199            memory: 16,
200            gpu: nvidia_gpu("GeForce GTX 1650"),
201            geo: IdentityCountryGeo::US,
202            language: vec!["en-US".into(), "en".into()],
203            history_count: Some(3),
204            proxy: None,
205            timezone: None
206        },
207        // 6 — Android / Chrome Mobile / Pixel 7 / Adreno 730
208        Identity {
209            id: Some(6),
210            device_model: Some("Pixel 7".into()),
211            has_battery: true,
212            has_mouse: false,
213            has_touch: true,
214            os: Os::Android,
215            os_version: "13".into(),
216            platform: PlatformInfo {
217                bitness: None,
218                architecture: None,
219                navigator_platform: NavigatorPlatform::LinuxArmV81,
220                version: "13".into(),
221            },
222            browser: Browser::Chrome,
223            browser_version: vec![124, 0, 6367, 82],
224            screen: screen(412, 915, 1080, 2400, 2.625),
225            hardware_concurrency: 8,
226            memory: 8,
227            gpu: Gpu {
228                vendor: "Qualcomm".into(),
229                webgl_renderer: "Adreno (TM) 730".into(),
230                webgl_vendor: "Qualcomm".into(),
231            },
232            geo: IdentityCountryGeo::US,
233            language: vec!["en-US".into(), "en".into()],
234            history_count: Some(2),
235            proxy: None,
236            timezone: None
237        },
238        // 7 — iOS / Safari Mobile / iPhone 15 / Apple GPU
239        Identity {
240            id: Some(7),
241            device_model: Some("iPhone 15".into()),
242            has_battery: true,
243            has_mouse: false,
244            has_touch: true,
245            os: Os::Ios,
246            os_version: "17.0".into(),
247            platform: PlatformInfo {
248                bitness: None,
249                architecture: None,
250                navigator_platform: NavigatorPlatform::IPhone,
251                version: "17.0".into(),
252            },
253            browser: Browser::Safari,
254            browser_version: vec![17, 0, 0, 0],
255            screen: screen(393, 852, 1179, 2556, 3.0),
256            hardware_concurrency: 6,
257            memory: 6,
258            gpu: apple_gpu("Apple A16 GPU"),
259            geo: IdentityCountryGeo::US,
260            language: vec!["en-US".into(), "en".into()],
261            history_count: Some(4),
262            proxy: None,
263            timezone: None
264        },
265        // 8 — Windows 11 / Chrome / Laptop + touch / AMD Radeon RX 6600
266        Identity {
267            id: Some(8),
268            device_model: None,
269            has_battery: true,
270            has_mouse: true,
271            has_touch: true,
272            os: Os::Windows,
273            os_version: "11.0.0".into(),
274            platform: win_platform("11.0.0"),
275            browser: Browser::Chrome,
276            browser_version: vec![123, 0, 6312, 122],
277            screen: screen(1920, 1200, 1920, 1200, 1.0),
278            hardware_concurrency: 8,
279            memory: 16,
280            gpu: amd_gpu("AMD Radeon RX 6600"),
281            geo: IdentityCountryGeo::FR,
282            language: vec!["fr-FR".into(), "fr".into(), "en".into()],
283            history_count: Some(9),
284            proxy: None,
285            timezone: None
286        },
287        // 9 — macOS Sonoma / Chrome / Desktop / Apple M3 Pro
288        Identity {
289            id: Some(9),
290            device_model: None,
291            has_battery: true,
292            has_mouse: true,
293            has_touch: false,
294            os: Os::Macintosh,
295            os_version: "14.2.1".into(),
296            platform: mac_platform("14.2.1"),
297            browser: Browser::Chrome,
298            browser_version: vec![124, 0, 6367, 91],
299            screen: screen(1512, 982, 3024, 1964, 2.0),
300            hardware_concurrency: 12,
301            memory: 36,
302            gpu: apple_gpu("Apple M3 Pro"),
303            geo: IdentityCountryGeo::DE,
304            language: vec!["de-DE".into(), "de".into(), "en".into()],
305            history_count: Some(15),
306            proxy: None,
307            timezone: None
308        },
309        // 10 — Android / Chrome Mobile / Samsung Galaxy S23 / Xclipse 920
310        Identity {
311            id: Some(10),
312            device_model: Some("SM-S911B".into()),
313            has_battery: true,
314            has_mouse: false,
315            has_touch: true,
316            os: Os::Android,
317            os_version: "14".into(),
318            platform: PlatformInfo {
319                bitness: None,
320                architecture: None,
321                navigator_platform: NavigatorPlatform::LinuxArmV81,
322                version: "14".into(),
323            },
324            browser: Browser::Chrome,
325            browser_version: vec![124, 0, 6367, 82],
326            screen: screen(360, 780, 1080, 2340, 3.0),
327            hardware_concurrency: 8,
328            memory: 8,
329            gpu: Gpu {
330                vendor: "Samsung".into(),
331                webgl_renderer: "Xclipse 920".into(),
332                webgl_vendor: "Samsung".into(),
333            },
334            geo: IdentityCountryGeo::US,
335            language: vec!["en-US".into(), "en".into()],
336            history_count: Some(6),
337            proxy: None,
338            timezone: None
339        },
340    ]
341}
342
343// ---------------------------------------------------------------------------
344// Public API
345// ---------------------------------------------------------------------------
346
347/// Returns all 10 built-in preset identities.
348pub fn presets() -> Vec<Identity> {
349    all().to_vec()
350}
351
352/// Fetch a preset identity by its numeric ID (1–10).
353///
354/// # Errors
355/// Returns [`IdentityError::NotFound`] if no preset has the given ID.
356pub fn get_by_id(id: u64) -> Result<Identity, IdentityError> {
357    all()
358        .into_iter()
359        .find(|p| p.id == Some(id))
360        .ok_or(IdentityError::NotFound(id))
361}
362
363/// Return a random preset identity.
364///
365/// Uses a simple entropy source based on the current system time so it
366/// requires no extra dependencies.
367pub fn random() -> Identity {
368    let idx = entropy_index(all().len());
369    all().into_iter().nth(idx).expect("preset index in bounds")
370}
371
372// ---------------------------------------------------------------------------
373// Tiny no-dep entropy helper (modulo distribution is fine for 10 items)
374// ---------------------------------------------------------------------------
375
376fn entropy_index(len: usize) -> usize {
377    use std::time::{SystemTime, UNIX_EPOCH};
378    let nanos = SystemTime::now()
379        .duration_since(UNIX_EPOCH)
380        .map(|d| d.subsec_nanos())
381        .unwrap_or(42);
382    (nanos as usize) % len
383}