statsig_rust/
statsig_user.rs

1use crate::dyn_value;
2use crate::evaluation::dynamic_value::DynamicValue;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6pub struct StatsigUserBuilder {
7    pub user_id: Option<DynamicValue>,
8    pub email: Option<DynamicValue>,
9    pub ip: Option<DynamicValue>,
10    pub user_agent: Option<DynamicValue>,
11    pub country: Option<DynamicValue>,
12    pub locale: Option<DynamicValue>,
13    pub app_version: Option<DynamicValue>,
14    pub custom: Option<HashMap<String, DynamicValue>>,
15    pub private_attributes: Option<HashMap<String, DynamicValue>>,
16    pub custom_ids: Option<HashMap<String, DynamicValue>>,
17}
18
19impl StatsigUserBuilder {
20    #[must_use]
21    pub fn new_with_user_id(user_id: String) -> Self {
22        Self {
23            user_id: Some(dyn_value!(user_id)),
24            ..Self::new()
25        }
26    }
27
28    #[must_use]
29    pub fn new_with_custom_ids<V>(custom_ids: HashMap<String, V>) -> Self
30    where
31        V: Into<DynamicValue>,
32    {
33        Self {
34            custom_ids: Some(custom_ids.into_iter().map(|(k, v)| (k, v.into())).collect()),
35            ..Self::new()
36        }
37    }
38
39    fn new() -> Self {
40        Self {
41            user_id: None,
42            email: None,
43            ip: None,
44            user_agent: None,
45            country: None,
46            locale: None,
47            app_version: None,
48            custom: None,
49            private_attributes: None,
50            custom_ids: None,
51        }
52    }
53
54    pub fn user_id(mut self, user_id: Option<String>) -> Self {
55        if let Some(user_id) = user_id {
56            self.user_id = Some(dyn_value!(user_id));
57        }
58        self
59    }
60
61    pub fn custom_ids(mut self, custom_ids: Option<HashMap<String, String>>) -> Self {
62        if let Some(custom_ids) = custom_ids {
63            self.custom_ids = Some(convert_str_map_to_dyn_values(custom_ids));
64        }
65        self
66    }
67
68    pub fn email(mut self, email: Option<String>) -> Self {
69        if let Some(email) = email {
70            self.email = Some(dyn_value!(email));
71        }
72        self
73    }
74
75    pub fn ip(mut self, ip: Option<String>) -> Self {
76        if let Some(ip) = ip {
77            self.ip = Some(dyn_value!(ip));
78        }
79        self
80    }
81
82    pub fn user_agent(mut self, user_agent: Option<String>) -> Self {
83        if let Some(user_agent) = user_agent {
84            self.user_agent = Some(dyn_value!(user_agent));
85        }
86        self
87    }
88
89    pub fn country(mut self, country: Option<String>) -> Self {
90        if let Some(country) = country {
91            self.country = Some(dyn_value!(country));
92        }
93        self
94    }
95
96    pub fn locale(mut self, locale: Option<String>) -> Self {
97        if let Some(locale) = locale {
98            self.locale = Some(dyn_value!(locale));
99        }
100        self
101    }
102
103    pub fn app_version(mut self, app_version: Option<String>) -> Self {
104        if let Some(app_version) = app_version {
105            self.app_version = Some(dyn_value!(app_version));
106        }
107        self
108    }
109
110    // todo: support HashMap<String, String | Number | Boolean | Array<String>>
111    pub fn custom_from_str_map(mut self, custom: Option<HashMap<String, String>>) -> Self {
112        if let Some(custom) = custom {
113            self.custom = Some(convert_str_map_to_dyn_values(custom));
114        }
115        self
116    }
117
118    pub fn custom(mut self, custom: Option<HashMap<String, DynamicValue>>) -> Self {
119        if let Some(custom) = custom {
120            self.custom = Some(custom);
121        }
122        self
123    }
124
125    // todo: support HashMap<String, String | Number | Boolean | Array<String>>
126    pub fn private_attributes_from_str_map(
127        mut self,
128        private_attributes: Option<HashMap<String, String>>,
129    ) -> Self {
130        if let Some(private_attributes) = private_attributes {
131            self.private_attributes = Some(convert_str_map_to_dyn_values(private_attributes));
132        }
133        self
134    }
135
136    pub fn private_attributes(
137        mut self,
138        private_attributes: Option<HashMap<String, DynamicValue>>,
139    ) -> Self {
140        if let Some(private_attributes) = private_attributes {
141            self.private_attributes = Some(private_attributes);
142        }
143        self
144    }
145
146    pub fn build(self) -> StatsigUser {
147        StatsigUser {
148            user_id: self.user_id,
149            email: self.email,
150            ip: self.ip,
151            user_agent: self.user_agent,
152            country: self.country,
153            locale: self.locale,
154            app_version: self.app_version,
155            custom: self.custom,
156            private_attributes: self.private_attributes,
157            custom_ids: self.custom_ids,
158        }
159    }
160}
161
162#[derive(Clone, Deserialize, Serialize)]
163#[serde(rename_all = "camelCase")]
164pub struct StatsigUser {
165    #[serde(rename = "userID", skip_serializing_if = "Option::is_none")]
166    pub user_id: Option<DynamicValue>,
167
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub email: Option<DynamicValue>,
170
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub ip: Option<DynamicValue>,
173
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub user_agent: Option<DynamicValue>,
176
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub country: Option<DynamicValue>,
179
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub locale: Option<DynamicValue>,
182
183    #[serde(skip_serializing_if = "Option::is_none")]
184    pub app_version: Option<DynamicValue>,
185
186    #[serde(skip_serializing_if = "Option::is_none")]
187    pub custom: Option<HashMap<String, DynamicValue>>,
188
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub private_attributes: Option<HashMap<String, DynamicValue>>,
191
192    #[serde(rename = "customIDs", skip_serializing_if = "Option::is_none")]
193    pub custom_ids: Option<HashMap<String, DynamicValue>>,
194}
195
196impl StatsigUser {
197    #[must_use]
198    pub fn with_user_id(user_id: String) -> Self {
199        StatsigUser {
200            user_id: Some(dyn_value!(user_id)),
201            ..Self::default()
202        }
203    }
204
205    #[must_use]
206    pub fn with_custom_ids<V>(custom_ids: HashMap<String, V>) -> Self
207    where
208        V: Into<DynamicValue>,
209    {
210        StatsigUser {
211            custom_ids: Some(custom_ids.into_iter().map(|(k, v)| (k, v.into())).collect()),
212            ..Self::default()
213        }
214    }
215
216    fn default() -> Self {
217        StatsigUser {
218            user_id: None,
219            email: None,
220            ip: None,
221            user_agent: None,
222            country: None,
223            locale: None,
224            app_version: None,
225            custom: None,
226            private_attributes: None,
227            custom_ids: None,
228        }
229    }
230}
231
232fn convert_str_map_to_dyn_values(
233    custom_ids: HashMap<String, String>,
234) -> HashMap<String, DynamicValue> {
235    custom_ids
236        .into_iter()
237        .map(|(k, v)| (k, dyn_value!(v)))
238        .collect()
239}