statsig_rust/
statsig_options.rs

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>>, // External DataStore
22
23    pub disable_all_logging: Option<bool>,
24    pub disable_country_lookup: Option<bool>,
25    pub disable_network: Option<bool>, // Disable all out-going network including get configs, log_events...
26    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>>, // Specs to customized spec adapter, order matters, reflecting priority of trying
54    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    // The builder method for more complex initialization
69    #[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    // Specs
87
88    #[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    // Event Logging
107
108    #[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    // ID Lists
162
163    #[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    // Other
188
189    #[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    // interface related options
268
269    #[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}