statsig_rust/user/
statsig_user_internal.rs1use std::collections::HashMap;
2
3use super::StatsigUserLoggable;
4use crate::evaluation::dynamic_value::DynamicValue;
5use crate::hashing::djb2_number;
6use crate::StatsigUser;
7use crate::{evaluation::dynamic_string::DynamicString, Statsig};
8
9pub type FullUserKey = (
10 u64, u64, u64, u64, u64, u64, u64, Vec<u64>, Vec<u64>, Vec<u64>, Vec<u64>, );
22
23#[derive(Clone)]
24pub struct StatsigUserInternal<'statsig, 'user> {
25 pub user_ref: &'user StatsigUser,
26 pub statsig_instance: Option<&'statsig Statsig>,
27}
28
29impl<'statsig, 'user> StatsigUserInternal<'statsig, 'user> {
30 pub fn new(user: &'user StatsigUser, statsig_instance: Option<&'statsig Statsig>) -> Self {
31 Self {
32 user_ref: user,
33 statsig_instance,
34 }
35 }
36
37 pub fn get_unit_id(&self, id_type: &DynamicString) -> Option<&DynamicValue> {
38 self.user_ref.get_unit_id(id_type)
39 }
40
41 pub fn get_user_value(&self, field: &Option<DynamicString>) -> Option<&DynamicValue> {
42 let field = field.as_ref()?;
43
44 let lowered_field = &field.lowercased_value;
45
46 let str_value = match lowered_field as &str {
47 "userid" => &self.user_ref.data.user_id,
48 "email" => &self.user_ref.data.email,
49 "ip" => &self.user_ref.data.ip,
50 "country" => &self.user_ref.data.country,
51 "locale" => &self.user_ref.data.locale,
52 "appversion" => &self.user_ref.data.app_version,
53 "useragent" => &self.user_ref.data.user_agent,
54 _ => &None,
55 };
56
57 if let Some(value) = str_value {
58 if let Some(str_val) = &value.string_value {
59 if !str_val.value.is_empty() {
60 return Some(value);
61 }
62 }
63 }
64
65 if let Some(custom) = &self.user_ref.data.custom {
66 if let Some(found) = custom.get(field.value.as_str()) {
67 return Some(found);
68 }
69 if let Some(lowered_found) = custom.get(lowered_field.as_str()) {
70 return Some(lowered_found);
71 }
72 }
73
74 if let Some(instance) = &self.statsig_instance {
75 if let Some(val) = instance.get_value_from_global_custom_fields(&field.value) {
76 return Some(val);
77 }
78
79 if let Some(val) = instance.get_value_from_global_custom_fields(&field.lowercased_value)
80 {
81 return Some(val);
82 }
83 }
84
85 if let Some(private_attributes) = &self.user_ref.data.private_attributes {
86 if let Some(found) = private_attributes.get(field.value.as_str()) {
87 return Some(found);
88 }
89 if let Some(lowered_found) = private_attributes.get(lowered_field.as_str()) {
90 return Some(lowered_found);
91 }
92 }
93
94 let str_value_alt = match lowered_field as &str {
95 "user_id" => &self.user_ref.data.user_id,
96 "app_version" => &self.user_ref.data.app_version,
97 "user_agent" => &self.user_ref.data.user_agent,
98 _ => &None,
99 };
100
101 if str_value_alt.is_some() {
102 return str_value_alt.as_ref();
103 }
104
105 None
106 }
107
108 pub fn get_value_from_environment(
109 &self,
110 field: &Option<DynamicString>,
111 ) -> Option<DynamicValue> {
112 let field = field.as_ref()?;
113
114 if let Some(statsig_environment) = &self.user_ref.data.statsig_environment {
115 if let Some(result) = statsig_environment.get(field.value.as_str()) {
116 return Some(result.clone());
117 }
118 }
119
120 if let Some(result) = self.statsig_instance?.get_from_statsig_env(&field.value) {
121 return Some(result);
122 }
123
124 self.statsig_instance?
125 .get_from_statsig_env(&field.lowercased_value)
126 }
127
128 pub fn to_loggable(&self) -> StatsigUserLoggable {
129 let mut environment = self.user_ref.data.statsig_environment.clone();
130 let mut global_custom: Option<HashMap<String, DynamicValue>> = None;
131
132 if let Some(statsig_instance) = &self.statsig_instance {
133 if environment.is_none() {
134 environment = statsig_instance.use_statsig_env(|e| e.cloned());
135 }
136 global_custom = statsig_instance.use_global_custom_fields(|gc| gc.cloned());
137 }
138
139 StatsigUserLoggable::new(&self.user_ref.data, environment, global_custom)
140 }
141
142 pub fn get_hashed_private_attributes(&self) -> Option<String> {
143 let private_attributes = match &self.user_ref.data.private_attributes {
144 Some(attrs) => attrs,
145 None => return None,
146 };
147
148 if private_attributes.is_empty() {
149 return None;
150 }
151
152 let mut val: i64 = 0;
153 for (key, value) in private_attributes {
154 let hash_key = match value.string_value {
155 Some(ref s) => key.to_owned() + ":" + &s.value,
156 None => key.to_owned() + ":",
157 };
158 val += djb2_number(&hash_key);
159 val &= 0xFFFF_FFFF;
160 }
161 Some(val.to_string())
162 }
163}