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::{
10 serialize_if_not_none, ObservabilityClient, OverrideAdapter, SpecAdapterConfig, SpecsAdapter,
11};
12use std::collections::HashMap;
13use std::fmt;
14use std::sync::{Arc, Weak};
15
16pub const DEFAULT_INIT_TIMEOUT_MS: u64 = 3000;
17
18#[derive(Clone, Default)]
19pub struct StatsigOptions {
20 pub data_store: Option<Arc<dyn DataStoreTrait>>, pub disable_all_logging: Option<bool>,
22 pub enable_id_lists: Option<bool>,
23 pub enable_user_agent_parsing: Option<bool>,
24 pub enable_country_lookup: Option<bool>,
25 pub environment: Option<String>,
26
27 pub event_logging_adapter: Option<Arc<dyn EventLoggingAdapter>>,
28 pub event_logging_flush_interval_ms: Option<u32>,
29 pub event_logging_max_queue_size: Option<u32>,
30
31 pub fallback_to_statsig_api: Option<bool>,
32
33 pub id_lists_adapter: Option<Arc<dyn IdListsAdapter>>,
34 pub id_lists_sync_interval_ms: Option<u32>,
35 pub id_lists_url: Option<String>,
36
37 pub init_timeout_ms: Option<u64>,
38 pub log_event_url: Option<String>,
39 pub observability_client: Option<Weak<dyn ObservabilityClient>>,
40 pub output_log_level: Option<LogLevel>,
41 pub override_adapter: Option<Arc<dyn OverrideAdapter>>,
42
43 pub spec_adapters_config: Option<Vec<SpecAdapterConfig>>, pub specs_adapter: Option<Arc<dyn SpecsAdapter>>,
45 pub specs_sync_interval_ms: Option<u32>,
46 pub specs_url: Option<String>,
47 pub service_name: Option<String>,
48 pub global_custom_fields: Option<HashMap<String, DynamicValue>>,
49}
50
51impl StatsigOptions {
52 #[must_use]
53 pub fn new() -> Self {
54 Self::default()
55 }
56
57 #[must_use]
59 pub fn builder() -> StatsigOptionsBuilder {
60 StatsigOptionsBuilder::default()
61 }
62}
63
64#[derive(Default)]
65pub struct StatsigOptionsBuilder {
66 inner: StatsigOptions,
67}
68
69impl StatsigOptionsBuilder {
70 #[must_use]
71 pub fn new() -> Self {
72 Self::default()
73 }
74
75 #[must_use]
78 pub fn specs_url(mut self, specs_url: Option<String>) -> Self {
79 self.inner.specs_url = specs_url;
80 self
81 }
82
83 #[must_use]
84 pub fn specs_adapter(mut self, specs_adapter: Option<Arc<dyn SpecsAdapter>>) -> Self {
85 self.inner.specs_adapter = specs_adapter;
86 self
87 }
88
89 #[must_use]
90 pub fn specs_sync_interval_ms(mut self, specs_sync_interval_ms: Option<u32>) -> Self {
91 self.inner.specs_sync_interval_ms = specs_sync_interval_ms;
92 self
93 }
94
95 #[must_use]
98 pub fn log_event_url(mut self, log_event_url: Option<String>) -> Self {
99 self.inner.log_event_url = log_event_url;
100 self
101 }
102
103 #[must_use]
104 pub fn disable_all_logging(mut self, disable_all_logging: Option<bool>) -> Self {
105 self.inner.disable_all_logging = disable_all_logging;
106 self
107 }
108
109 #[must_use]
110 pub fn event_logging_adapter(
111 mut self,
112 event_logging_adapter: Option<Arc<dyn EventLoggingAdapter>>,
113 ) -> Self {
114 self.inner.event_logging_adapter = event_logging_adapter;
115 self
116 }
117
118 #[must_use]
119 pub fn event_logging_flush_interval_ms(
120 mut self,
121 event_logging_flush_interval_ms: Option<u32>,
122 ) -> Self {
123 self.inner.event_logging_flush_interval_ms = event_logging_flush_interval_ms;
124 self
125 }
126
127 #[must_use]
128 pub fn event_logging_max_queue_size(
129 mut self,
130 event_logging_max_queue_size: Option<u32>,
131 ) -> Self {
132 self.inner.event_logging_max_queue_size = event_logging_max_queue_size;
133 self
134 }
135
136 #[must_use]
139 pub fn enable_id_lists(mut self, enable_id_lists: Option<bool>) -> Self {
140 self.inner.enable_id_lists = enable_id_lists;
141 self
142 }
143
144 #[must_use]
145 pub fn id_lists_url(mut self, id_lists_url: Option<String>) -> Self {
146 self.inner.id_lists_url = id_lists_url;
147 self
148 }
149
150 #[must_use]
151 pub fn id_lists_adapter(mut self, id_lists_adapter: Option<Arc<dyn IdListsAdapter>>) -> Self {
152 self.inner.id_lists_adapter = id_lists_adapter;
153 self
154 }
155
156 #[must_use]
157 pub fn id_lists_sync_interval_ms(mut self, id_lists_sync_interval_ms: Option<u32>) -> Self {
158 self.inner.id_lists_sync_interval_ms = id_lists_sync_interval_ms;
159 self
160 }
161
162 #[must_use]
165 pub fn environment(mut self, environment: Option<String>) -> Self {
166 self.inner.environment = environment;
167 self
168 }
169
170 #[must_use]
171 pub fn output_log_level(mut self, output_log_level: Option<u32>) -> Self {
172 if let Some(level) = output_log_level {
173 self.inner.output_log_level = Some(LogLevel::from(level));
174 }
175 self
176 }
177
178 #[must_use]
179 pub fn service_name(mut self, service_name: Option<String>) -> Self {
180 self.inner.service_name = service_name;
181 self
182 }
183
184 #[must_use]
185 pub fn fallback_to_statsig_api(mut self, fallback_to_statsig_api: Option<bool>) -> Self {
186 self.inner.fallback_to_statsig_api = fallback_to_statsig_api;
187 self
188 }
189
190 #[must_use]
191 pub fn enable_user_agent_parsing(mut self, enable_user_agent_parsing: Option<bool>) -> Self {
192 self.inner.enable_user_agent_parsing = enable_user_agent_parsing;
193 self
194 }
195
196 #[must_use]
197 pub fn enable_country_lookup(mut self, enable_country_lookup: Option<bool>) -> Self {
198 self.inner.enable_country_lookup = enable_country_lookup;
199 self
200 }
201
202 #[must_use]
203 pub fn global_custom_fields(
204 mut self,
205 global_custom_fields: Option<HashMap<String, DynamicValue>>,
206 ) -> Self {
207 self.inner.global_custom_fields = global_custom_fields;
208 self
209 }
210
211 #[must_use]
212 pub fn build(self) -> StatsigOptions {
213 self.inner
214 }
215}
216
217impl Serialize for StatsigOptions {
218 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
219 where
220 S: Serializer,
221 {
222 let mut state = serializer.serialize_struct("StatsigOptions", 20)?;
223 serialize_if_not_none!(state, "spec_url", &self.specs_url);
224 serialize_if_not_none!(
225 state,
226 "spec_adapter",
227 &get_display_name(&self.specs_adapter)
228 );
229 serialize_if_not_none!(state, "spec_adapter_configs", &self.spec_adapters_config);
230 serialize_if_not_none!(
231 state,
232 "specs_sync_interval_ms",
233 &self.specs_sync_interval_ms
234 );
235 serialize_if_not_none!(state, "init_timeout_ms", &self.init_timeout_ms);
236
237 serialize_if_not_none!(state, "data_store", &get_if_set(&self.data_store));
238
239 serialize_if_not_none!(state, "log_event_url", &self.log_event_url);
240 serialize_if_not_none!(state, "disable_all_logging", &self.disable_all_logging);
241
242 serialize_if_not_none!(state, "id_lists_url", &self.id_lists_url);
243 serialize_if_not_none!(state, "enable_id_lists", &self.enable_id_lists);
244 serialize_if_not_none!(
245 state,
246 "enable_user_agent_parsing",
247 &self.enable_user_agent_parsing
248 );
249 serialize_if_not_none!(state, "enable_country_lookup", &self.enable_country_lookup);
250 serialize_if_not_none!(
251 state,
252 "id_lists_sync_interval",
253 &self.id_lists_sync_interval_ms
254 );
255 serialize_if_not_none!(state, "environment", &self.environment);
256 serialize_if_not_none!(
257 state,
258 "id_list_adapter",
259 &get_display_name(&self.id_lists_adapter)
260 );
261 serialize_if_not_none!(
262 state,
263 "fallback_to_statsig_api",
264 &self.fallback_to_statsig_api
265 );
266 serialize_if_not_none!(
267 state,
268 "override_adapter",
269 &get_if_set(&self.override_adapter)
270 );
271 serialize_if_not_none!(state, "service_name", &get_if_set(&self.service_name));
272 serialize_if_not_none!(state, "global_custom_fields", &self.global_custom_fields);
273
274 state.end()
275 }
276}
277
278fn get_if_set<T>(s: &Option<T>) -> Option<&str> {
279 s.as_ref().map(|_| "set")
280}
281
282fn get_display_name<T: fmt::Debug>(s: &Option<T>) -> Option<String> {
283 s.as_ref().map(|st| format!("{st:?}"))
284}