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