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