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