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