Skip to main content

statsig_rust/user/
statsig_user.rs

1use 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
54// -------------------------------------------------------------------------------- [Getters/Setters]
55
56macro_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    // ---------------------------------------- [User ID]
112
113    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    // ---------------------------------------- [Custom IDs]
129
130    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    // ---------------------------------------- [Unit ID]
157
158    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    // ---------------------------------------- [ Statsig Environment ]
173
174    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 Fields ]
210
211    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 Fields ]
219
220    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
229// -------------------------------------------------------------------------------- [Helpers]
230
231fn 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}