statsig_rust/user/
statsig_user.rs

1use 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
46// -------------------------------------------------------------------------------- [Getters/Setters]
47
48macro_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
174// -------------------------------------------------------------------------------- [Helpers]
175
176fn 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}