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