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::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>>, // External DataStore
23
24    pub disable_all_logging: Option<bool>,
25    pub disable_country_lookup: Option<bool>,
26    pub disable_network: Option<bool>, // Disable all out-going network including get configs, log_events...
27    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>>, // Specs to customized spec adapter, order matters, reflecting priority of trying
55    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    // The builder method for more complex initialization
72    #[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    // Specs
90
91    #[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    // Event Logging
110
111    #[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    // ID Lists
165
166    #[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    // Other
191
192    #[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    // interface related options
277
278    #[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}