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    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>>, // Specs to customized spec adapter, order matters, reflecting priority of trying
52    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    // The builder method for more complex initialization
67    #[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    // Specs
85
86    #[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    // Event Logging
105
106    #[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    // ID Lists
159
160    #[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    // Other
185
186    #[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    // interface related options
265
266    #[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}