statsig_rust/user/
statsig_user.rs1use crate::evaluation::dynamic_value::DynamicValue;
2use crate::statsig_metadata;
3use crate::{dyn_value, evaluation::dynamic_string::DynamicString};
4use std::{collections::HashMap, sync::Arc};
5
6use super::{
7 into_optional::IntoOptional,
8 unit_id::UnitID,
9 user_data::{UserData, UserDataMap},
10};
11
12#[derive(Clone)]
13pub struct StatsigUser {
14 pub data: Arc<UserData>,
15
16 pub(crate) sdk_version: &'static str,
17}
18
19impl StatsigUser {
20 #[must_use]
21 pub fn with_user_id(user_id: impl Into<UnitID>) -> Self {
22 let unit_id: UnitID = user_id.into();
23 Self::new(UserData {
24 user_id: Some(unit_id.into()),
25 ..UserData::default()
26 })
27 }
28
29 #[must_use]
30 pub fn with_custom_ids<K, U>(custom_ids: HashMap<K, U>) -> Self
31 where
32 K: Into<String>,
33 U: Into<UnitID>,
34 {
35 let custom_ids: UserDataMap = custom_ids
36 .into_iter()
37 .map(|(k, v)| (k.into(), v.into().into()))
38 .collect();
39
40 Self::new(UserData {
41 custom_ids: Some(custom_ids),
42 ..UserData::default()
43 })
44 }
45
46 pub fn new(inner: UserData) -> Self {
47 Self {
48 data: Arc::new(inner),
49 sdk_version: statsig_metadata::SDK_VERSION,
50 }
51 }
52}
53
54macro_rules! string_field_accessor {
57 ($self:ident, $getter_name:ident, $setter_name:ident, $field:ident) => {
58 pub fn $getter_name(&self) -> Option<&str> {
59 self.data
60 .$field
61 .as_ref()?
62 .string_value
63 .as_ref()
64 .map(|s| s.value.as_str())
65 }
66
67 pub fn $setter_name(&mut self, value: impl IntoOptional<String>) {
68 let value = value.into_optional();
69 let mut_data = Arc::make_mut(&mut self.data);
70 match value {
71 Some(value) => {
72 mut_data.$field = Some(dyn_value!(value));
73 }
74 None => mut_data.$field = None,
75 }
76 }
77 };
78}
79
80macro_rules! map_field_accessor {
81 ($self:ident, $getter_name:ident, $setter_name:ident, $field:ident) => {
82 pub fn $getter_name(&self) -> Option<&UserDataMap> {
83 self.data.$field.as_ref()
84 }
85
86 pub fn $setter_name<K, V>(&mut self, value: impl IntoOptional<HashMap<K, V>>)
87 where
88 K: Into<String>,
89 V: Into<DynamicValue>,
90 {
91 let mut_data = Arc::make_mut(&mut self.data);
92 let value = match value.into_optional() {
93 Some(value) => value,
94 None => {
95 mut_data.$field = None;
96 return;
97 }
98 };
99
100 mut_data.$field = Some(
101 value
102 .into_iter()
103 .map(|(k, v)| (k.into(), v.into()))
104 .collect(),
105 );
106 }
107 };
108}
109
110impl StatsigUser {
111 pub fn get_user_id(&self) -> Option<&str> {
114 self.data
115 .user_id
116 .as_ref()?
117 .string_value
118 .as_ref()
119 .map(|s| s.value.as_str())
120 }
121
122 pub fn set_user_id(&mut self, user_id: impl Into<UnitID>) {
123 let unit_id = user_id.into();
124 let mut_data = Arc::make_mut(&mut self.data);
125 mut_data.user_id = Some(unit_id.into());
126 }
127
128 pub fn get_custom_ids(&self) -> Option<HashMap<&str, &str>> {
131 let mapped = self
132 .data
133 .custom_ids
134 .as_ref()?
135 .iter()
136 .map(entry_to_key_value_refs)
137 .collect();
138
139 Some(mapped)
140 }
141
142 pub fn set_custom_ids<K, U>(&mut self, custom_ids: HashMap<K, U>)
143 where
144 K: Into<String>,
145 U: Into<UnitID>,
146 {
147 let custom_ids = custom_ids
148 .into_iter()
149 .map(|(k, v)| (k.into(), v.into().into()))
150 .collect();
151
152 let mut_data = Arc::make_mut(&mut self.data);
153 mut_data.custom_ids = Some(custom_ids);
154 }
155
156 pub fn get_unit_id(&self, id_type: &DynamicString) -> Option<&DynamicValue> {
159 if id_type.lowercased_value.eq("userid") {
160 return self.data.user_id.as_ref();
161 }
162
163 let custom_ids = self.data.custom_ids.as_ref()?;
164
165 if let Some(custom_id) = custom_ids.get(id_type.value.as_str()) {
166 return Some(custom_id);
167 }
168
169 custom_ids.get(id_type.lowercased_value.as_str())
170 }
171
172 pub fn get_statsig_environment(&self) -> Option<HashMap<&str, &str>> {
175 let mapped = self
176 .data
177 .statsig_environment
178 .as_ref()?
179 .iter()
180 .map(entry_to_key_value_refs)
181 .collect();
182
183 Some(mapped)
184 }
185
186 pub fn set_statsig_environment<K, U>(&mut self, statsig_environment: Option<HashMap<K, U>>)
187 where
188 K: Into<String>,
189 U: Into<String>,
190 {
191 let mut_data = Arc::make_mut(&mut self.data);
192
193 let statsig_environment = match statsig_environment {
194 Some(v) => v,
195 None => {
196 mut_data.statsig_environment = None;
197 return;
198 }
199 };
200
201 let statsig_environment: UserDataMap = statsig_environment
202 .into_iter()
203 .map(|(k, v)| (k.into(), v.into().into()))
204 .collect();
205
206 mut_data.statsig_environment = Some(statsig_environment);
207 }
208
209 string_field_accessor!(self, get_email, set_email, email);
212 string_field_accessor!(self, get_ip, set_ip, ip);
213 string_field_accessor!(self, get_user_agent, set_user_agent, user_agent);
214 string_field_accessor!(self, get_country, set_country, country);
215 string_field_accessor!(self, get_locale, set_locale, locale);
216 string_field_accessor!(self, get_app_version, set_app_version, app_version);
217
218 map_field_accessor!(self, get_custom, set_custom, custom);
221 map_field_accessor!(
222 self,
223 get_private_attributes,
224 set_private_attributes,
225 private_attributes
226 );
227}
228
229fn entry_to_key_value_refs<'a>(entry: (&'a String, &'a DynamicValue)) -> (&'a str, &'a str) {
232 let (key, value) = entry;
233
234 (
235 key.as_str(),
236 value
237 .string_value
238 .as_ref()
239 .map(|s| s.value.as_str())
240 .unwrap_or(""),
241 )
242}