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