1use std::time::Duration;
2
3use serde::{Deserialize, Serialize};
4use serde_with::skip_serializing_none;
5use std::collections::HashMap;
6
7#[derive(Serialize, Deserialize)]
8pub struct StatsigConfig<T> {
9 pub value: Option<T>,
10 pub name: String,
11 pub group_name: Option<String>,
12 pub rule_id: String,
13 pub group: String,
14}
15
16#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
17#[serde(rename_all = "camelCase")]
18pub struct StatsigPost {
19 pub events: Vec<StatsigEvent>,
20}
21
22#[derive(Debug, PartialEq, Clone, Eq, Deserialize, Serialize)]
23#[serde(rename_all = "camelCase")]
24pub struct StatsigEvent {
25 pub event_name: String,
26 pub value: String,
27 pub time: String, pub user: StatsigUser,
29 pub metadata: HashMap<String, String>,
30 }
32
33#[skip_serializing_none]
34#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
35#[serde(rename_all = "camelCase")]
36pub struct StatsigUser {
37 #[serde(rename = "userID")]
38 pub user_id: String,
39 pub email: Option<String>,
40 pub ip: Option<String>,
41 pub user_agent: Option<String>,
42 pub country: Option<String>,
43 pub locale: Option<String>,
44 pub app_version: Option<String>,
45 pub custom: Option<HashMap<String, String>>,
46 pub private_atributes: Option<HashMap<String, String>>,
47 #[serde(rename = "customIDs")]
48 pub custom_ids: Option<HashMap<String, String>>,
49 pub statsig_environment: StatsigEnvironment,
50}
51
52pub struct StatsigOptions {
57 pub api_url: Option<String>,
58 pub cdn_url: Option<String>,
59 pub events_url: Option<String>,
60 pub disable_cache: bool,
61 pub config_sync_interval: Option<Duration>,
62}
63
64impl StatsigOptions {
65 pub fn default() -> Self {
66 Self {
67 api_url: None,
68 cdn_url: None,
69 disable_cache: false,
70 config_sync_interval: None,
71 events_url: None,
72 }
73 }
74
75 pub fn cache_disabled() -> Self {
76 Self {
77 api_url: None,
78 cdn_url: None,
79 disable_cache: true,
80 config_sync_interval: None,
81 events_url: None,
82 }
83 }
84}
85
86#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
87#[serde(rename_all = "camelCase")]
88pub struct StatsigEnvironment {
89 pub tier: String,
90}
91
92impl StatsigEnvironment {
93 pub fn get_field(&self, field: &str) -> String {
94 let empty = "".to_string();
95 match field.to_ascii_lowercase().as_str() {
96 "tier" => self.tier.clone(),
97 _ => empty,
98 }
99 }
100}
101
102impl StatsigUser {
103 pub fn new(user_id: String, tier: String) -> Self {
104 StatsigUser {
105 user_id,
106 email: None,
107 ip: None,
108 user_agent: None,
109 country: None,
110 locale: None,
111 app_version: None,
112 custom: None,
113 private_atributes: None,
114 custom_ids: None,
115 statsig_environment: StatsigEnvironment { tier },
116 }
117 }
118
119 pub fn get_unit_id(&self, id_type: &String) -> String {
121 if id_type.to_ascii_lowercase() == *"userid" {
122 return self.user_id.clone();
123 }
124 if let Some(custom_ids) = &self.custom_ids {
125 if custom_ids.contains_key(id_type) {
126 return custom_ids.get(id_type).unwrap_or(&self.user_id).clone();
127 }
128 let lower = id_type.to_ascii_lowercase();
129 if custom_ids.contains_key(&lower) {
130 return custom_ids.get(&lower).unwrap_or(&self.user_id).clone();
131 }
132 }
133 self.user_id.clone()
134 }
135
136 pub fn get_field(&self, field: &String) -> String {
137 let empty = "".to_string();
138 match field.to_ascii_lowercase().as_str() {
139 "userid" | "user_id" => self.user_id.clone(),
140 "email" => self.email.as_ref().unwrap_or(&empty).clone(),
141 "ip" | "ipaddress" | "ip_address" => self.ip.as_ref().unwrap_or(&empty).clone(),
142 "useragent" | "user_agent" => self.user_agent.as_ref().unwrap_or(&empty).clone(),
143 "country" => self.country.as_ref().unwrap_or(&empty).clone(),
144 "locale" => self.locale.as_ref().unwrap_or(&empty).clone(),
145 "appversion" | "app_version" => self.app_version.as_ref().unwrap_or(&empty).clone(),
146 _ => {
147 let mut ret = "".to_string();
148 if let Some(custom) = &self.custom {
149 if let Some(v) = custom.get(field) {
150 ret = v.clone();
151 } else if let Some(v) = custom.get(&field.to_ascii_lowercase()) {
152 ret = v.clone();
153 }
154 } else if let Some(private) = &self.private_atributes {
155 if let Some(v) = private.get(field) {
156 ret = v.clone();
157 } else if let Some(v) = private.get(&field.to_ascii_lowercase()) {
158 ret = v.clone();
159 }
160 }
161 ret
162 }
163 }
164 }
165}
166
167#[cfg(test)]
168mod test {
169 use std::collections::HashMap;
170
171 use crate::models::StatsigEnvironment;
172
173 use super::StatsigUser;
174
175 #[test]
176 fn test_get_unit_id_default_user() {
177 let user = StatsigUser::new("user_id".to_string(), "prod".to_string());
178 assert_eq!(
179 user.get_unit_id(&"userid".to_string()),
180 "user_id".to_string()
181 );
182 assert_eq!(
183 user.get_unit_id(&"not_userid".to_string()),
184 "user_id".to_string()
185 );
186 }
187
188 #[test]
189 fn test_get_unit_id_custom_ids() {
190 let mut user = StatsigUser::new("user_id".to_string(), "prod".to_string());
191 let mut custom_ids = HashMap::new();
192 custom_ids.insert("not_userid".to_string(), "not_userid".to_string());
193 custom_ids.insert("ALL_CAPS".to_string(), "ALL_CAPS".to_string());
194 user.custom_ids = Some(custom_ids);
195 assert_eq!(
196 user.get_unit_id(&"userid".to_string()),
197 "user_id".to_string()
198 );
199 assert_eq!(
200 user.get_unit_id(&"Not_userid".to_string()),
201 "not_userid".to_string()
202 );
203 assert_eq!(
204 user.get_unit_id(&"ALL_CAPS".to_string()),
205 "ALL_CAPS".to_string()
206 );
207 assert_eq!(
208 user.get_unit_id(&"non_existing".to_string()),
209 "user_id".to_string()
210 );
211 }
212
213 #[test]
214 fn test_get_field() {
215 let user = StatsigUser {
216 user_id: "userid".to_string(),
217 email: Some("abc@email.com".to_string()),
218 ip: Some("192.168.0.1".to_string()),
219 user_agent: None,
220 country: None,
221 locale: None,
222 app_version: None,
223 custom: Some(HashMap::from([("custom1".to_string(), "val1".to_string())])),
224 private_atributes: None,
225 custom_ids: None,
226 statsig_environment: StatsigEnvironment {
227 tier: "prod".to_string(),
228 },
229 };
230 assert_eq!("userid".to_string(), user.get_field(&"userID".to_string()));
231 assert_eq!(
232 "abc@email.com".to_string(),
233 user.get_field(&"email".to_string())
234 );
235 assert_eq!("192.168.0.1".to_string(), user.get_field(&"ip".to_string()));
236 assert_eq!("val1".to_string(), user.get_field(&"custom1".to_string()));
237 }
238}