use rand::RngExt;
use rand::seq::IndexedRandom;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BrowserProfile {
pub user_agent: String,
pub platform: String,
pub hardware_concurrency: u32,
pub device_memory: u32,
pub webgl_vendor: String,
pub webgl_renderer: String,
pub viewport_width: u32,
pub viewport_height: u32,
pub accept_language: String,
}
impl BrowserProfile {
pub fn random() -> Self {
let mut rng = rand::rng();
let user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
];
let webgl_vendors = [
"Google Inc. (NVIDIA)",
"Google Inc. (Apple)",
"Google Inc. (Intel)",
];
let webgl_renderers = [
"ANGLE (NVIDIA, NVIDIA GeForce RTX 3070 Direct3D11 vs_5_0 ps_5_0, D3D11)",
"ANGLE (NVIDIA, NVIDIA GeForce RTX 3080 Direct3D11 vs_5_0 ps_5_0, D3D11)",
"ANGLE (Apple, Apple M2, OpenGL 4.1)",
"ANGLE (Intel, Intel(R) Iris(R) Xe Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)",
];
let concurrency = [4, 8, 12, 16];
let memory = [8, 16, 32];
let dimensions = [(1920, 1080), (2560, 1440), (1366, 768), (1440, 900)];
let chosen_ua = user_agents.choose(&mut rng).unwrap();
let chosen_platform = if chosen_ua.contains("Windows") {
"Win32"
} else if chosen_ua.contains("Macintosh") {
"MacIntel"
} else {
"Linux x86_64"
};
let chosen_vendor = if chosen_ua.contains("Macintosh") {
webgl_vendors[1]
} else {
webgl_vendors[0]
};
let chosen_renderer = if chosen_ua.contains("Macintosh") {
webgl_renderers[2]
} else if chosen_ua.contains("Windows") {
if rng.random_bool(0.5) {
webgl_renderers[0]
} else {
webgl_renderers[3]
}
} else {
webgl_renderers[0]
};
let (width, height) = dimensions.choose(&mut rng).unwrap();
Self {
user_agent: chosen_ua.to_string(),
platform: chosen_platform.to_string(),
hardware_concurrency: *concurrency.choose(&mut rng).unwrap(),
device_memory: *memory.choose(&mut rng).unwrap(),
webgl_vendor: chosen_vendor.to_string(),
webgl_renderer: chosen_renderer.to_string(),
viewport_width: *width,
viewport_height: *height,
accept_language: "en-US,en;q=0.9".to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_random_browser_profile() {
let profile = BrowserProfile::random();
assert!(!profile.user_agent.is_empty());
assert!(!profile.platform.is_empty());
assert!(profile.hardware_concurrency > 0);
assert!(profile.device_memory > 0);
assert!(!profile.webgl_vendor.is_empty());
assert!(!profile.webgl_renderer.is_empty());
assert!(profile.viewport_width >= 1024);
assert!(profile.viewport_height >= 768);
assert_eq!(profile.accept_language, "en-US,en;q=0.9");
}
#[test]
fn test_profile_serialization() {
let profile = BrowserProfile::random();
let json = serde_json::to_string(&profile).expect("Failed to serialize");
let deserialized: BrowserProfile =
serde_json::from_str(&json).expect("Failed to deserialize");
assert_eq!(profile.user_agent, deserialized.user_agent);
assert_eq!(profile.platform, deserialized.platform);
}
#[test]
fn test_realistic_profile_bindings() {
for _ in 0..100 {
let profile = BrowserProfile::random();
if profile.user_agent.contains("Macintosh") {
assert_eq!(profile.platform, "MacIntel");
assert_eq!(profile.webgl_vendor, "Google Inc. (Apple)");
assert!(profile.webgl_renderer.contains("Apple"));
} else if profile.user_agent.contains("Windows") {
assert_eq!(profile.platform, "Win32");
assert_eq!(profile.webgl_vendor, "Google Inc. (NVIDIA)");
assert!(
profile.webgl_renderer.contains("NVIDIA")
|| profile.webgl_renderer.contains("Intel")
);
} else {
assert_eq!(profile.platform, "Linux x86_64");
assert_eq!(profile.webgl_vendor, "Google Inc. (NVIDIA)");
}
assert!(
profile.hardware_concurrency == 4
|| profile.hardware_concurrency == 8
|| profile.hardware_concurrency == 12
|| profile.hardware_concurrency == 16
);
assert!(
profile.device_memory == 8
|| profile.device_memory == 16
|| profile.device_memory == 32
);
}
}
}