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 proxy_config(mut self, proxy_config: Option<ProxyConfig>) -> Self {
194 self.inner.proxy_config = proxy_config;
195 self
196 }
197
198 #[must_use]
199 pub fn environment(mut self, environment: Option<String>) -> Self {
200 self.inner.environment = environment;
201 self
202 }
203
204 #[must_use]
205 pub fn output_log_level(mut self, output_log_level: Option<u32>) -> Self {
206 if let Some(level) = output_log_level {
207 self.inner.output_log_level = Some(LogLevel::from(level));
208 }
209 self
210 }
211
212 #[must_use]
213 pub fn wait_for_country_lookup_init(
214 mut self,
215 wait_for_country_lookup_init: Option<bool>,
216 ) -> Self {
217 self.inner.wait_for_country_lookup_init = wait_for_country_lookup_init;
218 self
219 }
220
221 #[must_use]
222 pub fn wait_for_user_agent_init(mut self, wait_for_user_agent_init: Option<bool>) -> Self {
223 self.inner.wait_for_user_agent_init = wait_for_user_agent_init;
224 self
225 }
226
227 #[must_use]
228 pub fn disable_user_agent_parsing(mut self, disable_user_agent_parsing: Option<bool>) -> Self {
229 self.inner.disable_user_agent_parsing = disable_user_agent_parsing;
230 self
231 }
232
233 #[must_use]
234 pub fn disable_country_lookup(mut self, disable_country_lookup: Option<bool>) -> Self {
235 self.inner.disable_country_lookup = disable_country_lookup;
236 self
237 }
238
239 #[must_use]
240 pub fn service_name(mut self, service_name: Option<String>) -> Self {
241 self.inner.service_name = service_name;
242 self
243 }
244
245 #[must_use]
246 pub fn fallback_to_statsig_api(mut self, fallback_to_statsig_api: Option<bool>) -> Self {
247 self.inner.fallback_to_statsig_api = fallback_to_statsig_api;
248 self
249 }
250
251 #[must_use]
252 pub fn global_custom_fields(
253 mut self,
254 global_custom_fields: Option<HashMap<String, DynamicValue>>,
255 ) -> Self {
256 self.inner.global_custom_fields = global_custom_fields;
257 self
258 }
259
260 pub fn disable_network(mut self, disable_network: Option<bool>) -> Self {
261 self.inner.disable_network = disable_network;
262 self
263 }
264
265 #[must_use]
266 pub fn init_timeout_ms(mut self, init_timeout_ms: Option<u64>) -> Self {
267 self.inner.init_timeout_ms = init_timeout_ms;
268 self
269 }
270
271 #[must_use]
272 pub fn build(self) -> StatsigOptions {
273 self.inner
274 }
275
276 #[must_use]
279 pub fn observability_client(mut self, client: Option<Weak<dyn ObservabilityClient>>) -> Self {
280 self.inner.observability_client = client;
281 self
282 }
283
284 #[must_use]
285 pub fn data_store(mut self, data_store: Option<Arc<dyn DataStoreTrait>>) -> Self {
286 self.inner.data_store = data_store;
287 self
288 }
289}
290
291impl Serialize for StatsigOptions {
292 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
293 where
294 S: Serializer,
295 {
296 let mut state = serializer.serialize_struct("StatsigOptions", 20)?;
297 serialize_if_not_none!(state, "spec_url", &self.specs_url);
298 serialize_if_not_none!(
299 state,
300 "spec_adapter",
301 &get_display_name(&self.specs_adapter)
302 );
303 serialize_if_not_none!(state, "spec_adapter_configs", &self.spec_adapters_config);
304 serialize_if_not_none!(
305 state,
306 "specs_sync_interval_ms",
307 &self.specs_sync_interval_ms
308 );
309 serialize_if_not_none!(state, "init_timeout_ms", &self.init_timeout_ms);
310
311 serialize_if_not_none!(state, "data_store", &get_if_set(&self.data_store));
312
313 serialize_if_not_none!(state, "log_event_url", &self.log_event_url);
314 serialize_if_not_none!(state, "disable_all_logging", &self.disable_all_logging);
315 serialize_if_not_none!(state, "disable_network", &self.disable_network);
316
317 serialize_if_not_none!(state, "id_lists_url", &self.id_lists_url);
318 serialize_if_not_none!(state, "enable_id_lists", &self.enable_id_lists);
319 serialize_if_not_none!(
320 state,
321 "wait_for_user_agent_init",
322 &self.wait_for_user_agent_init
323 );
324 serialize_if_not_none!(
325 state,
326 "wait_for_country_lookup_init",
327 &self.wait_for_country_lookup_init
328 );
329 serialize_if_not_none!(
330 state,
331 "id_lists_sync_interval",
332 &self.id_lists_sync_interval_ms
333 );
334 serialize_if_not_none!(state, "environment", &self.environment);
335 serialize_if_not_none!(
336 state,
337 "id_list_adapter",
338 &get_display_name(&self.id_lists_adapter)
339 );
340 serialize_if_not_none!(
341 state,
342 "fallback_to_statsig_api",
343 &self.fallback_to_statsig_api
344 );
345 serialize_if_not_none!(
346 state,
347 "override_adapter",
348 &get_if_set(&self.override_adapter)
349 );
350 serialize_if_not_none!(state, "service_name", &get_if_set(&self.service_name));
351 serialize_if_not_none!(state, "global_custom_fields", &self.global_custom_fields);
352
353 state.end()
354 }
355}
356
357fn get_if_set<T>(s: &Option<T>) -> Option<&str> {
358 s.as_ref().map(|_| "set")
359}
360
361fn get_display_name<T: fmt::Debug>(s: &Option<T>) -> Option<String> {
362 s.as_ref().map(|st| format!("{st:?}"))
363}