Skip to main content

rustenium_identity/
identity.rs

1use serde::{Deserialize, Serialize};
2
3/// Top-level identity record. Serialises to/from the MongoDB document
4/// schema produced by persona_generator.py.
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub enum IdentityCountryGeo {
7    US,
8    UK,
9    JP,
10    DE,
11    FR,
12    CA,
13    AU,
14    IN,
15    BR,
16    KR,
17    IT,
18    ES,
19    NL,
20    PL,
21    SE,
22    MX,
23    SG,
24    ZA,
25}
26
27impl IdentityCountryGeo {
28    pub fn as_str(&self) -> &'static str {
29        match self {
30            Self::US => "US",
31            Self::UK => "UK",
32            Self::JP => "JP",
33            Self::DE => "DE",
34            Self::FR => "FR",
35            Self::CA => "CA",
36            Self::AU => "AU",
37            Self::IN => "IN",
38            Self::BR => "BR",
39            Self::KR => "KR",
40            Self::IT => "IT",
41            Self::ES => "ES",
42            Self::NL => "NL",
43            Self::PL => "PL",
44            Self::SE => "SE",
45            Self::MX => "MX",
46            Self::SG => "SG",
47            Self::ZA => "ZA",
48        }
49    }
50}
51
52impl TryFrom<&str> for IdentityCountryGeo {
53    type Error = String;
54
55    fn try_from(s: &str) -> Result<Self, Self::Error> {
56        match s {
57            "US" => Ok(Self::US),
58            "UK" => Ok(Self::UK),
59            "JP" => Ok(Self::JP),
60            "DE" => Ok(Self::DE),
61            "FR" => Ok(Self::FR),
62            "CA" => Ok(Self::CA),
63            "AU" => Ok(Self::AU),
64            "IN" => Ok(Self::IN),
65            "BR" => Ok(Self::BR),
66            "KR" => Ok(Self::KR),
67            "IT" => Ok(Self::IT),
68            "ES" => Ok(Self::ES),
69            "NL" => Ok(Self::NL),
70            "PL" => Ok(Self::PL),
71            "SE" => Ok(Self::SE),
72            "MX" => Ok(Self::MX),
73            "SG" => Ok(Self::SG),
74            "ZA" => Ok(Self::ZA),
75            other => Err(format!("unknown country code: {other}")),
76        }
77    }
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct Identity {
82    pub id: Option<u64>,
83
84    /// None device_model implies desktop/laptop
85    pub device_model: Option<String>,
86    pub has_battery: bool,
87    pub has_mouse: bool,
88    pub has_touch: bool,
89
90    pub os: Os,
91    pub os_version: String,
92    pub platform: PlatformInfo,
93
94    pub browser: Browser,
95    /// e.g. [124, 0, 6367, 78]
96    pub browser_version: Vec<u16>,
97
98    pub screen: ScreenResolution,
99    pub hardware_concurrency: u8,
100    /// deviceMemory in GiB
101    pub memory: u8,
102    pub gpu: Gpu,
103
104    pub geo: IdentityCountryGeo,
105
106    /// e.g. ["en-US", "en"]
107    pub language: Vec<String>,
108    pub history_count: Option<u16>,
109    /// Full URL with credentials
110    pub proxy: Option<String>,
111    /// IANA timezone string. If None, fetched from ip-api.com via proxy.
112    pub timezone: Option<String>,
113}
114
115impl Identity {
116    /// Deserialize from a raw JSON string (MongoDB extended JSON).
117    pub fn from_json(s: &str) -> Result<Self, serde_json::Error> {
118        serde_json::from_str(s)
119    }
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct PlatformInfo {
124    pub bitness: Option<String>,
125    pub architecture: Option<String>,
126    pub navigator_platform: NavigatorPlatform,
127    pub version: String,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub enum NavigatorPlatform {
132    #[serde(rename = "Win32")]
133    Win32,
134    #[serde(rename = "MacIntel")]
135    MacIntel,
136    #[serde(rename = "Linux x86_64")]
137    LinuxX86_64,
138    #[serde(rename = "Linux armv81")]
139    LinuxArmV81,
140    #[serde(rename = "iPhone")]
141    IPhone,
142    Other(String),
143}
144
145impl NavigatorPlatform {
146    pub fn as_str(&self) -> &str {
147        match self {
148            Self::Win32 => "Win32",
149            Self::MacIntel => "MacIntel",
150            Self::LinuxX86_64 => "Linux x86_64",
151            Self::LinuxArmV81 => "Linux armv81",
152            Self::IPhone => "iPhone",
153            Self::Other(s) => s.as_str(),
154        }
155    }
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct ScreenResolution {
160    pub logical_width: u16,
161    pub logical_height: u16,
162    pub original_width: u16,
163    pub original_height: u16,
164    pub density_pixel_ratio: f32,
165}
166
167/// GPU identity — drives WebGL parameter spoofing.
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct Gpu {
170    pub vendor: String,
171    /// e.g. "ANGLE (NVIDIA GeForce RTX 3080...)"
172    pub webgl_renderer: String,
173    /// Exact UNMASKED_VENDOR_WEBGL string, e.g. "Google Inc. (NVIDIA)"
174    pub webgl_vendor: String,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub enum Os {
179    Windows,
180    Macintosh,
181    Linux,
182    Android,
183    Ios,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
187#[serde(rename_all = "lowercase")]
188pub enum Browser {
189    Chrome,
190    Safari,
191    Edge,
192}