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::{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
50// -------------------------------------------------------------------------------- [Getters/Setters]
51
52macro_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    // ---------------------------------------- [User ID]
108
109    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    // ---------------------------------------- [Custom IDs]
125
126    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    // ---------------------------------------- [Unit ID]
153
154    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    // ---------------------------------------- [ Statsig Environment ]
169
170    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 Fields ]
206
207    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 Fields ]
215
216    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
225// -------------------------------------------------------------------------------- [Helpers]
226
227fn 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}