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