1use serde::ser::SerializeStruct;
2use serde::{Serialize, Serializer};
3
4use crate::data_store_interface::DataStoreTrait;
5use crate::evaluation::dynamic_value::DynamicValue;
6use crate::event_logging_adapter::EventLoggingAdapter;
7use crate::id_lists_adapter::IdListsAdapter;
8use crate::networking::proxy_config::ProxyConfig;
9use crate::output_logger::LogLevel;
10use crate::persistent_storage::persistent_storage_trait::PersistentStorage;
11use crate::{
12 serialize_if_not_none, ObservabilityClient, OverrideAdapter, SpecAdapterConfig, SpecsAdapter,
13};
14use std::collections::HashMap;
15use std::fmt;
16use std::sync::{Arc, Weak};
17
18pub const DEFAULT_INIT_TIMEOUT_MS: u64 = 3000;
19
20#[derive(Clone, Default)]
21pub struct StatsigOptions {
22 pub data_store: Option<Arc<dyn DataStoreTrait>>, pub disable_all_logging: Option<bool>,
25 pub disable_country_lookup: Option<bool>,
26 pub disable_network: Option<bool>, pub disable_user_agent_parsing: Option<bool>,
28
29 pub enable_id_lists: Option<bool>,
30 pub environment: Option<String>,
31
32 pub event_logging_adapter: Option<Arc<dyn EventLoggingAdapter>>,
33
34 #[deprecated]
35 pub event_logging_flush_interval_ms: Option<u32>,
36 pub event_logging_max_pending_batch_queue_size: Option<u32>,
37 pub event_logging_max_queue_size: Option<u32>,
38
39 pub fallback_to_statsig_api: Option<bool>,
40 pub global_custom_fields: Option<HashMap<String, DynamicValue>>,
41
42 pub id_lists_adapter: Option<Arc<dyn IdListsAdapter>>,
43 pub id_lists_sync_interval_ms: Option<u32>,
44 pub id_lists_url: Option<String>,
45
46 pub init_timeout_ms: Option<u64>,
47 pub log_event_url: Option<String>,
48 pub observability_client: Option<Weak<dyn ObservabilityClient>>,
49 pub output_log_level: Option<LogLevel>,
50 pub override_adapter: Option<Arc<dyn OverrideAdapter>>,
51 pub persistent_storage: Option<Arc<dyn PersistentStorage>>,
52 pub service_name: Option<String>,
53
54 pub spec_adapters_config: Option<Vec<SpecAdapterConfig>>, pub specs_adapter: Option<Arc<dyn SpecsAdapter>>,
56 pub specs_sync_interval_ms: Option<u32>,
57 pub specs_url: Option<String>,
58
59 pub wait_for_country_lookup_init: Option<bool>,
60 pub wait_for_user_agent_init: Option<bool>,
61
62 pub proxy_config: Option<ProxyConfig>,
63}
64
65impl StatsigOptions {
66 #[must_use]
67 pub fn new() -> Self {
68 Self::default()
69 }
70
71 #[must_use]
73 pub fn builder() -> StatsigOptionsBuilder {
74 StatsigOptionsBuilder::default()
75 }
76}
77
78#[derive(Default)]
79pub struct StatsigOptionsBuilder {
80 inner: StatsigOptions,
81}
82
83impl StatsigOptionsBuilder {
84 #[must_use]
85 pub fn new() -> Self {
86 Self::default()
87 }
88
89 #[must_use]
92 pub fn specs_url(mut self, specs_url: Option<String>) -> Self {
93 self.inner.specs_url = specs_url;
94 self
95 }
96
97 #[must_use]
98 pub fn specs_adapter(mut self, specs_adapter: Option<Arc<dyn SpecsAdapter>>) -> Self {
99 self.inner.specs_adapter = specs_adapter;
100 self
101 }
102
103 #[must_use]
104 pub fn specs_sync_interval_ms(mut self, specs_sync_interval_ms: Option<u32>) -> Self {
105 self.inner.specs_sync_interval_ms = specs_sync_interval_ms;
106 self
107 }
108
109 #[must_use]
112 pub fn log_event_url(mut self, log_event_url: Option<String>) -> Self {
113 self.inner.log_event_url = log_event_url;
114 self
115 }
116
117 #[must_use]
118 pub fn disable_all_logging(mut self, disable_all_logging: Option<bool>) -> Self {
119 self.inner.disable_all_logging = disable_all_logging;
120 self
121 }
122
123 #[must_use]
124 pub fn event_logging_adapter(
125 mut self,
126 event_logging_adapter: Option<Arc<dyn EventLoggingAdapter>>,
127 ) -> Self {
128 self.inner.event_logging_adapter = event_logging_adapter;
129 self
130 }
131
132 #[must_use]
133 #[deprecated(
134 note = "This field is deprecated in favor of smart log event. It is no longer consumed and can be removed safely."
135 )]
136 #[allow(deprecated)]
137 pub fn event_logging_flush_interval_ms(
138 mut self,
139 event_logging_flush_interval_ms: Option<u32>,
140 ) -> Self {
141 self.inner.event_logging_flush_interval_ms = event_logging_flush_interval_ms;
142 self
143 }
144
145 #[must_use]
146 pub fn event_logging_max_queue_size(
147 mut self,
148 event_logging_max_queue_size: Option<u32>,
149 ) -> Self {
150 self.inner.event_logging_max_queue_size = event_logging_max_queue_size;
151 self
152 }
153
154 #[must_use]
155 pub fn event_logging_max_pending_batch_queue_size(
156 mut self,
157 event_logging_max_pending_batch_queue_size: Option<u32>,
158 ) -> Self {
159 self.inner.event_logging_max_pending_batch_queue_size =
160 event_logging_max_pending_batch_queue_size;
161 self
162 }
163
164 #[must_use]
167 pub fn enable_id_lists(mut self, enable_id_lists: Option<bool>) -> Self {
168 self.inner.enable_id_lists = enable_id_lists;
169 self
170 }
171
172 #[must_use]
173 pub fn id_lists_url(mut self, id_lists_url: Option<String>) -> Self {
174 self.inner.id_lists_url = id_lists_url;
175 self
176 }
177
178 #[must_use]
179 pub fn id_lists_adapter(mut self, id_lists_adapter: Option<Arc<dyn IdListsAdapter>>) -> Self {
180 self.inner.id_lists_adapter = id_lists_adapter;
181 self
182 }
183
184 #[must_use]
185 pub fn id_lists_sync_interval_ms(mut self, id_lists_sync_interval_ms: Option<u32>) -> Self {
186 self.inner.id_lists_sync_interval_ms = id_lists_sync_interval_ms;
187 self
188 }
189
190 #[must_use]
193 pub fn environment(mut self, environment: Option<String>) -> Self {
194 self.inner.environment = environment;
195 self
196 }
197
198 #[must_use]
199 pub fn output_log_level(mut self, output_log_level: Option<u32>) -> Self {
200 if let Some(level) = output_log_level {
201 self.inner.output_log_level = Some(LogLevel::from(level));
202 }
203 self
204 }
205
206 #[must_use]
207 pub fn wait_for_country_lookup_init(
208 mut self,
209 wait_for_country_lookup_init: Option<bool>,
210 ) -> Self {
211 self.inner.wait_for_country_lookup_init = wait_for_country_lookup_init;
212 self
213 }
214
215 #[must_use]
216 pub fn wait_for_user_agent_init(mut self, wait_for_user_agent_init: Option<bool>) -> Self {
217 self.inner.wait_for_user_agent_init = wait_for_user_agent_init;
218 self
219 }
220
221 #[must_use]
222 pub fn disable_user_agent_parsing(mut self, disable_user_agent_parsing: Option<bool>) -> Self {
223 self.inner.disable_user_agent_parsing = disable_user_agent_parsing;
224 self
225 }
226
227 #[must_use]
228 pub fn disable_country_lookup(mut self, disable_country_lookup: Option<bool>) -> Self {
229 self.inner.disable_country_lookup = disable_country_lookup;
230 self
231 }
232
233 #[must_use]
234 pub fn service_name(mut self, service_name: Option<String>) -> Self {
235 self.inner.service_name = service_name;
236 self
237 }
238
239 #[must_use]
240 pub fn fallback_to_statsig_api(mut self, fallback_to_statsig_api: Option<bool>) -> Self {
241 self.inner.fallback_to_statsig_api = fallback_to_statsig_api;
242 self
243 }
244
245 #[must_use]
246 pub fn global_custom_fields(
247 mut self,
248 global_custom_fields: Option<HashMap<String, DynamicValue>>,
249 ) -> Self {
250 self.inner.global_custom_fields = global_custom_fields;
251 self
252 }
253
254 pub fn disable_network(mut self, disable_network: Option<bool>) -> Self {
255 self.inner.disable_network = disable_network;
256 self
257 }
258
259 #[must_use]
260 pub fn init_timeout_ms(mut self, init_timeout_ms: Option<u64>) -> Self {
261 self.inner.init_timeout_ms = init_timeout_ms;
262 self
263 }
264
265 #[must_use]
266 pub fn build(self) -> StatsigOptions {
267 self.inner
268 }
269
270 #[must_use]
273 pub fn observability_client(mut self, client: Option<Weak<dyn ObservabilityClient>>) -> Self {
274 self.inner.observability_client = client;
275 self
276 }
277
278 #[must_use]
279 pub fn data_store(mut self, data_store: Option<Arc<dyn DataStoreTrait>>) -> Self {
280 self.inner.data_store = data_store;
281 self
282 }
283}
284
285impl Serialize for StatsigOptions {
286 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
287 where
288 S: Serializer,
289 {
290 let mut state = serializer.serialize_struct("StatsigOptions", 20)?;
291 serialize_if_not_none!(state, "spec_url", &self.specs_url);
292 serialize_if_not_none!(
293 state,
294 "spec_adapter",
295 &get_display_name(&self.specs_adapter)
296 );
297 serialize_if_not_none!(state, "spec_adapter_configs", &self.spec_adapters_config);
298 serialize_if_not_none!(
299 state,
300 "specs_sync_interval_ms",
301 &self.specs_sync_interval_ms
302 );
303 serialize_if_not_none!(state, "init_timeout_ms", &self.init_timeout_ms);
304
305 serialize_if_not_none!(state, "data_store", &get_if_set(&self.data_store));
306
307 serialize_if_not_none!(state, "log_event_url", &self.log_event_url);
308 serialize_if_not_none!(state, "disable_all_logging", &self.disable_all_logging);
309 serialize_if_not_none!(state, "disable_network", &self.disable_network);
310
311 serialize_if_not_none!(state, "id_lists_url", &self.id_lists_url);
312 serialize_if_not_none!(state, "enable_id_lists", &self.enable_id_lists);
313 serialize_if_not_none!(
314 state,
315 "wait_for_user_agent_init",
316 &self.wait_for_user_agent_init
317 );
318 serialize_if_not_none!(
319 state,
320 "wait_for_country_lookup_init",
321 &self.wait_for_country_lookup_init
322 );
323 serialize_if_not_none!(
324 state,
325 "id_lists_sync_interval",
326 &self.id_lists_sync_interval_ms
327 );
328 serialize_if_not_none!(state, "environment", &self.environment);
329 serialize_if_not_none!(
330 state,
331 "id_list_adapter",
332 &get_display_name(&self.id_lists_adapter)
333 );
334 serialize_if_not_none!(
335 state,
336 "fallback_to_statsig_api",
337 &self.fallback_to_statsig_api
338 );
339 serialize_if_not_none!(
340 state,
341 "override_adapter",
342 &get_if_set(&self.override_adapter)
343 );
344 serialize_if_not_none!(state, "service_name", &get_if_set(&self.service_name));
345 serialize_if_not_none!(state, "global_custom_fields", &self.global_custom_fields);
346
347 state.end()
348 }
349}
350
351fn get_if_set<T>(s: &Option<T>) -> Option<&str> {
352 s.as_ref().map(|_| "set")
353}
354
355fn get_display_name<T: fmt::Debug>(s: &Option<T>) -> Option<String> {
356 s.as_ref().map(|st| format!("{st:?}"))
357}