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
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}