rustenium-identity 0.1.9

A versatile stealth overlay for rustenium
Documentation
//! Built-in identity presets.
//!
//! Provides 10 realistic browser/device identities that can be retrieved by
//! their numeric ID or selected at random, without any external dependency.
//!
//! # Example
//! ```rust,no_run
//! use rustenium_identity::preset;
//!
//! let id = preset::get_by_id(1).unwrap();
//! let rnd = preset::random();
//! println!("{:?}", rnd.browser);
//! ```

use crate::error::IdentityError;
use crate::identity::{
    Browser, Gpu, Identity, IdentityCountryGeo, NavigatorPlatform, Os, PlatformInfo,
    ScreenResolution,
};

// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------

fn win_platform(ver: &str) -> PlatformInfo {
    PlatformInfo {
        bitness: Some("64".into()),
        architecture: Some("x86".into()),
        navigator_platform: NavigatorPlatform::Win32,
        version: ver.into(),
    }
}

fn mac_platform(ver: &str) -> PlatformInfo {
    PlatformInfo {
        bitness: Some("64".into()),
        architecture: Some("arm".into()),
        navigator_platform: NavigatorPlatform::MacIntel,
        version: ver.into(),
    }
}

fn lin_platform(ver: &str) -> PlatformInfo {
    PlatformInfo {
        bitness: Some("64".into()),
        architecture: Some("x86".into()),
        navigator_platform: NavigatorPlatform::LinuxX86_64,
        version: ver.into(),
    }
}

fn screen(lw: u16, lh: u16, ow: u16, oh: u16, dpr: f32) -> ScreenResolution {
    ScreenResolution {
        logical_width: lw,
        logical_height: lh,
        original_width: ow,
        original_height: oh,
        density_pixel_ratio: dpr,
    }
}

fn nvidia_gpu(model: &str) -> Gpu {
    Gpu {
        vendor: "NVIDIA Corporation".into(),
        webgl_renderer: format!("ANGLE (NVIDIA {} Direct3D11 vs_5_0 ps_5_0)", model),
        webgl_vendor: "Google Inc. (NVIDIA)".into(),
    }
}

fn intel_gpu(model: &str) -> Gpu {
    Gpu {
        vendor: "Intel Inc.".into(),
        webgl_renderer: format!("ANGLE (Intel, {} Direct3D11 vs_5_0 ps_5_0)", model),
        webgl_vendor: "Google Inc. (Intel)".into(),
    }
}

fn apple_gpu(model: &str) -> Gpu {
    Gpu {
        vendor: "Apple".into(),
        webgl_renderer: model.into(),
        webgl_vendor: "Apple".into(),
    }
}

fn amd_gpu(model: &str) -> Gpu {
    Gpu {
        vendor: "Advanced Micro Devices, Inc.".into(),
        webgl_renderer: format!("ANGLE (AMD, {} Direct3D11 vs_5_0 ps_5_0)", model),
        webgl_vendor: "Google Inc. (AMD)".into(),
    }
}

// ---------------------------------------------------------------------------
// Preset table (IDs 1-10)
// ---------------------------------------------------------------------------

fn all() -> [Identity; 10] {
    [
        // 1 — Windows 11 / Chrome / Desktop / NVIDIA RTX 3080
        Identity {
            id: Some(1),
            device_model: None,
            has_battery: false,
            has_mouse: true,
            has_touch: false,
            os: Os::Windows,
            os_version: "15.0.0".into(),
            platform: win_platform("15.0.0"),
            browser: Browser::Chrome,
            browser_version: vec![124, 0, 6367, 78],
            screen: screen(1280, 720, 1920, 1080, 1.0),
            hardware_concurrency: 16,
            memory: 16,
            gpu: nvidia_gpu("GeForce RTX 3080"),
            geo: IdentityCountryGeo::US,
            language: vec!["en-US".into(), "en".into()],
            history_count: Some(12),
            proxy: None,
            timezone: None,
        },
        // 2 — Windows 10 / Edge / Desktop / Intel UHD 630
        Identity {
            id: Some(2),
            device_model: None,
            has_battery: false,
            has_mouse: true,
            has_touch: false,
            os: Os::Windows,
            os_version: "10.0.0".into(),
            platform: win_platform("10.0.0"),
            browser: Browser::Edge,
            browser_version: vec![123, 0, 2420, 65],
            screen: screen(1366, 768, 1366, 768, 1.0),
            hardware_concurrency: 8,
            memory: 8,
            gpu: intel_gpu("Intel(R) UHD Graphics 630"),
            geo: IdentityCountryGeo::US,
            language: vec!["en-US".into(), "en".into()],
            history_count: Some(5),
            proxy: None,
            timezone: None,
        },
        // 3 — macOS Ventura / Safari / Desktop / Apple M2
        Identity {
            id: Some(3),
            device_model: None,
            has_battery: true,
            has_mouse: true,
            has_touch: false,
            os: Os::Macintosh,
            os_version: "13.4.1".into(),
            platform: mac_platform("13.4.1"),
            browser: Browser::Safari,
            browser_version: vec![17, 0, 0, 0],
            screen: screen(2560, 1600, 2560, 1600, 2.0),
            hardware_concurrency: 10,
            memory: 16,
            gpu: apple_gpu("Apple M2"),
            geo: IdentityCountryGeo::UK,
            language: vec!["en-GB".into(), "en".into()],
            history_count: Some(20),
            proxy: None,
            timezone: None,
        },
        // 4 — macOS Monterey / Chrome / Desktop / Apple M1
        Identity {
            id: Some(4),
            device_model: None,
            has_battery: true,
            has_mouse: true,
            has_touch: false,
            os: Os::Macintosh,
            os_version: "12.6.0".into(),
            platform: mac_platform("12.6.0"),
            browser: Browser::Chrome,
            browser_version: vec![124, 0, 6367, 82],
            screen: screen(1440, 900, 2880, 1800, 2.0),
            hardware_concurrency: 8,
            memory: 8,
            gpu: apple_gpu("Apple M1"),
            geo: IdentityCountryGeo::US,
            language: vec!["en-US".into(), "en".into()],
            history_count: Some(7),
            proxy: None,
            timezone: None,
        },
        // 5 — Ubuntu Linux / Chrome / Desktop / NVIDIA GTX 1650
        Identity {
            id: Some(5),
            device_model: None,
            has_battery: false,
            has_mouse: true,
            has_touch: false,
            os: Os::Linux,
            os_version: "22.04".into(),
            platform: lin_platform("22.04"),
            browser: Browser::Chrome,
            browser_version: vec![124, 0, 6367, 79],
            screen: screen(1920, 1080, 1920, 1080, 1.0),
            hardware_concurrency: 12,
            memory: 16,
            gpu: nvidia_gpu("GeForce GTX 1650"),
            geo: IdentityCountryGeo::US,
            language: vec!["en-US".into(), "en".into()],
            history_count: Some(3),
            proxy: None,
            timezone: None,
        },
        // 6 — Android / Chrome Mobile / Pixel 7 / Adreno 730
        Identity {
            id: Some(6),
            device_model: Some("Pixel 7".into()),
            has_battery: true,
            has_mouse: false,
            has_touch: true,
            os: Os::Android,
            os_version: "13".into(),
            platform: PlatformInfo {
                bitness: None,
                architecture: None,
                navigator_platform: NavigatorPlatform::LinuxArmV81,
                version: "13".into(),
            },
            browser: Browser::Chrome,
            browser_version: vec![124, 0, 6367, 82],
            screen: screen(412, 915, 1080, 2400, 2.625),
            hardware_concurrency: 8,
            memory: 8,
            gpu: Gpu {
                vendor: "Qualcomm".into(),
                webgl_renderer: "Adreno (TM) 730".into(),
                webgl_vendor: "Qualcomm".into(),
            },
            geo: IdentityCountryGeo::US,
            language: vec!["en-US".into(), "en".into()],
            history_count: Some(2),
            proxy: None,
            timezone: None,
        },
        // 7 — iOS / Safari Mobile / iPhone 15 / Apple GPU
        Identity {
            id: Some(7),
            device_model: Some("iPhone 15".into()),
            has_battery: true,
            has_mouse: false,
            has_touch: true,
            os: Os::Ios,
            os_version: "17.0".into(),
            platform: PlatformInfo {
                bitness: None,
                architecture: None,
                navigator_platform: NavigatorPlatform::IPhone,
                version: "17.0".into(),
            },
            browser: Browser::Safari,
            browser_version: vec![17, 0, 0, 0],
            screen: screen(393, 852, 1179, 2556, 3.0),
            hardware_concurrency: 6,
            memory: 6,
            gpu: apple_gpu("Apple A16 GPU"),
            geo: IdentityCountryGeo::US,
            language: vec!["en-US".into(), "en".into()],
            history_count: Some(4),
            proxy: None,
            timezone: None,
        },
        // 8 — Windows 11 / Chrome / Laptop + touch / AMD Radeon RX 6600
        Identity {
            id: Some(8),
            device_model: None,
            has_battery: true,
            has_mouse: true,
            has_touch: true,
            os: Os::Windows,
            os_version: "11.0.0".into(),
            platform: win_platform("11.0.0"),
            browser: Browser::Chrome,
            browser_version: vec![123, 0, 6312, 122],
            screen: screen(1920, 1200, 1920, 1200, 1.0),
            hardware_concurrency: 8,
            memory: 16,
            gpu: amd_gpu("AMD Radeon RX 6600"),
            geo: IdentityCountryGeo::FR,
            language: vec!["fr-FR".into(), "fr".into(), "en".into()],
            history_count: Some(9),
            proxy: None,
            timezone: None,
        },
        // 9 — macOS Sonoma / Chrome / Desktop / Apple M3 Pro
        Identity {
            id: Some(9),
            device_model: None,
            has_battery: true,
            has_mouse: true,
            has_touch: false,
            os: Os::Macintosh,
            os_version: "14.2.1".into(),
            platform: mac_platform("14.2.1"),
            browser: Browser::Chrome,
            browser_version: vec![124, 0, 6367, 91],
            screen: screen(1512, 982, 3024, 1964, 2.0),
            hardware_concurrency: 12,
            memory: 36,
            gpu: apple_gpu("Apple M3 Pro"),
            geo: IdentityCountryGeo::DE,
            language: vec!["de-DE".into(), "de".into(), "en".into()],
            history_count: Some(15),
            proxy: None,
            timezone: None,
        },
        // 10 — Android / Chrome Mobile / Samsung Galaxy S23 / Xclipse 920
        Identity {
            id: Some(10),
            device_model: Some("SM-S911B".into()),
            has_battery: true,
            has_mouse: false,
            has_touch: true,
            os: Os::Android,
            os_version: "14".into(),
            platform: PlatformInfo {
                bitness: None,
                architecture: None,
                navigator_platform: NavigatorPlatform::LinuxArmV81,
                version: "14".into(),
            },
            browser: Browser::Chrome,
            browser_version: vec![124, 0, 6367, 82],
            screen: screen(360, 780, 1080, 2340, 3.0),
            hardware_concurrency: 8,
            memory: 8,
            gpu: Gpu {
                vendor: "Qualcomm".into(),
                webgl_renderer: "ANGLE (Qualcomm, Adreno (TM) 750, OpenGL ES 3.2)".into(),
                webgl_vendor: "Google Inc. (Qualcomm)".into(),
            },
            geo: IdentityCountryGeo::US,
            language: vec!["en-US".into(), "en".into()],
            history_count: Some(6),
            proxy: None,
            timezone: None,
        },
    ]
}

// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------

/// Returns all 10 built-in preset identities.
pub fn presets() -> Vec<Identity> {
    all().to_vec()
}

/// Fetch a preset identity by its numeric ID (1–10).
///
/// # Errors
/// Returns [`IdentityError::NotFound`] if no preset has the given ID.
pub fn get_by_id(id: u64) -> Result<Identity, IdentityError> {
    all()
        .into_iter()
        .find(|p| p.id == Some(id))
        .ok_or(IdentityError::NotFound(id))
}

/// Return a random preset identity.
///
/// Uses a simple entropy source based on the current system time so it
/// requires no extra dependencies.
pub fn random() -> Identity {
    let idx = entropy_index(all().len());
    all().into_iter().nth(idx).expect("preset index in bounds")
}

// ---------------------------------------------------------------------------
// Tiny no-dep entropy helper (modulo distribution is fine for 10 items)
// ---------------------------------------------------------------------------

fn entropy_index(len: usize) -> usize {
    use std::time::{SystemTime, UNIX_EPOCH};
    let nanos = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|d| d.subsec_nanos())
        .unwrap_or(42);
    (nanos as usize) % len
}