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
54fn push_string_field_hashes(hashes: &mut Vec<u64>, field: &Option<DynamicValue>) {
55    if let Some(field) = field {
56        hashes.push(field.hash_value);
57    } else {
58        hashes.push(0);
59    }
60}
61
62fn push_map_field_hashes(hashes: &mut Vec<u64>, field: &Option<HashMap<String, DynamicValue>>) {
63    if let Some(field) = field {
64        hashes.extend(field.values().map(|id| id.hash_value));
65    } else {
66        hashes.extend(EMPTY_HASHES);
67    }
68}