statsig_rust/user/
user_data.rs

1use 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
24    #[serde(skip_serializing)]
25    pub private_attributes: Option<HashMap<String, DynamicValue>>,
26    pub custom: Option<HashMap<String, DynamicValue>>,
27}
28
29impl UserData {
30    pub fn create_user_values_hash(&self) -> u64 {
31        let hashes = self.get_all_user_hashes();
32        hashing::hash_one(hashes)
33    }
34
35    fn get_all_user_hashes(&self) -> Vec<u64> {
36        let mut hashes = Vec::new();
37        push_string_field_hashes(&mut hashes, &self.user_id);
38        push_map_field_hashes(&mut hashes, &self.custom_ids);
39
40        push_string_field_hashes(&mut hashes, &self.app_version);
41        push_string_field_hashes(&mut hashes, &self.country);
42        push_string_field_hashes(&mut hashes, &self.email);
43        push_string_field_hashes(&mut hashes, &self.ip);
44        push_string_field_hashes(&mut hashes, &self.locale);
45        push_string_field_hashes(&mut hashes, &self.user_agent);
46
47        push_map_field_hashes(&mut hashes, &self.custom);
48        push_map_field_hashes(&mut hashes, &self.private_attributes);
49
50        hashes
51    }
52
53    pub fn to_bytes(&self) -> Option<Vec<u8>> {
54        serde_json::to_vec(self).ok()
55    }
56}
57
58fn push_string_field_hashes(hashes: &mut Vec<u64>, field: &Option<DynamicValue>) {
59    if let Some(field) = field {
60        hashes.push(field.hash_value);
61    } else {
62        hashes.push(0);
63    }
64}
65
66fn push_map_field_hashes(hashes: &mut Vec<u64>, field: &Option<HashMap<String, DynamicValue>>) {
67    if let Some(field) = field {
68        hashes.extend(field.values().map(|id| id.hash_value));
69    } else {
70        hashes.extend(EMPTY_HASHES);
71    }
72}