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