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    /// Deserialize from a BSON Document.
122    #[cfg(feature = "bson")]
123    pub fn from_bson(doc: bson::Document) -> Result<Self, bson::de::Error> {
124        bson::from_document(doc)
125    }
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct PlatformInfo {
130    pub bitness: Option<String>,
131    pub architecture: Option<String>,
132    pub navigator_platform: NavigatorPlatform,
133    pub version: String,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
137pub enum NavigatorPlatform {
138    #[serde(rename = "Win32")]
139    Win32,
140    #[serde(rename = "MacIntel")]
141    MacIntel,
142    #[serde(rename = "Linux x86_64")]
143    LinuxX86_64,
144    #[serde(rename = "Linux armv81")]
145    LinuxArmV81,
146    #[serde(rename = "iPhone")]
147    IPhone,
148    Other(String),
149}
150
151impl NavigatorPlatform {
152    pub fn as_str(&self) -> &str {
153        match self {
154            Self::Win32 => "Win32",
155            Self::MacIntel => "MacIntel",
156            Self::LinuxX86_64 => "Linux x86_64",
157            Self::LinuxArmV81 => "Linux armv81",
158            Self::IPhone => "iPhone",
159            Self::Other(s) => s.as_str(),
160        }
161    }
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct ScreenResolution {
166    pub logical_width: u16,
167    pub logical_height: u16,
168    pub original_width: u16,
169    pub original_height: u16,
170    pub density_pixel_ratio: f32,
171}
172
173/// GPU identity — drives WebGL parameter spoofing.
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct Gpu {
176    pub vendor: String,
177    /// e.g. "ANGLE (NVIDIA GeForce RTX 3080...)"
178    pub webgl_renderer: String,
179    /// Exact UNMASKED_VENDOR_WEBGL string, e.g. "Google Inc. (NVIDIA)"
180    pub webgl_vendor: String,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub enum Os {
185    Windows,
186    Macintosh,
187    Linux,
188    Android,
189    Ios,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
193#[serde(rename_all = "lowercase")]
194pub enum Browser {
195    Chrome,
196    Safari,
197    Edge,
198}