statsig_rust/user/
user_data.rs1use crate::{evaluation::dynamic_value::DynamicValue, hashing};
2use serde::{Deserialize, Serialize};
3use serde_with::skip_serializing_none;
4use std::collections::HashMap;
5
6const EMPTY_HASHES: &[u64] = &[0];
7
8#[skip_serializing_none]
9#[derive(Clone, Deserialize, Serialize, Default)]
10#[serde(rename_all = "camelCase")]
11pub struct UserData {
12 #[serde(rename = "userID")]
13 pub user_id: Option<DynamicValue>,
14 #[serde(rename = "customIDs")]
15 pub custom_ids: Option<HashMap<String, DynamicValue>>,
16
17 pub email: Option<DynamicValue>,
18 pub ip: Option<DynamicValue>,
19 pub user_agent: Option<DynamicValue>,
20 pub country: Option<DynamicValue>,
21 pub locale: Option<DynamicValue>,
22 pub app_version: Option<DynamicValue>,
23 pub statsig_environment: Option<HashMap<String, DynamicValue>>,
24
25 #[serde(skip_serializing)]
26 pub private_attributes: Option<HashMap<String, DynamicValue>>,
27 pub custom: Option<HashMap<String, DynamicValue>>,
28}
29
30impl UserData {
31 pub fn create_user_values_hash(&self) -> u64 {
32 let hashes = self.get_all_user_hashes();
33 hashing::hash_one(hashes)
34 }
35
36 fn get_all_user_hashes(&self) -> Vec<u64> {
37 let mut hashes = Vec::new();
38 push_string_field_hashes(&mut hashes, &self.user_id);
39 push_map_field_hashes(&mut hashes, &self.custom_ids);
40
41 push_string_field_hashes(&mut hashes, &self.app_version);
42 push_string_field_hashes(&mut hashes, &self.country);
43 push_string_field_hashes(&mut hashes, &self.email);
44 push_string_field_hashes(&mut hashes, &self.ip);
45 push_string_field_hashes(&mut hashes, &self.locale);
46 push_string_field_hashes(&mut hashes, &self.user_agent);
47
48 push_map_field_hashes(&mut hashes, &self.custom);
49 push_map_field_hashes(&mut hashes, &self.private_attributes);
50
51 hashes
52 }
53
54 pub fn to_bytes(&self) -> Option<Vec<u8>> {
55 serde_json::to_vec(self).ok()
56 }
57}
58
59fn push_string_field_hashes(hashes: &mut Vec<u64>, field: &Option<DynamicValue>) {
60 if let Some(field) = field {
61 hashes.push(field.hash_value);
62 } else {
63 hashes.push(0);
64 }
65}
66
67fn push_map_field_hashes(hashes: &mut Vec<u64>, field: &Option<HashMap<String, DynamicValue>>) {
68 if let Some(field) = field {
69 hashes.extend(field.values().map(|id| id.hash_value));
70 } else {
71 hashes.extend(EMPTY_HASHES);
72 }
73}