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