1use crate::evaluation::evaluation_details::EvaluationDetails;
2use crate::evaluation::evaluation_types::{
3 AnyEvaluation, DynamicConfigEvaluation, ExperimentEvaluation, GateEvaluation, LayerEvaluation,
4};
5use crate::event_logging::event_logger::{EventLogger, QueuedEventPayload};
6use crate::event_logging::layer_exposure::LayerExposure;
7use crate::sampling_processor::SamplingDecision;
8use crate::specs_response::param_store_types::Parameter;
9use crate::statsig_core_api_options::ParameterStoreEvaluationOptions;
10use crate::user::StatsigUserLoggable;
11use crate::StatsigUser;
12use crate::{SamplingProcessor, Statsig};
13
14use serde::de::DeserializeOwned;
15use serde::{Deserialize, Serialize};
16use serde_json::{from_value, Value};
17use std::collections::HashMap;
18use std::sync::Weak;
19
20#[derive(Serialize, Deserialize, Clone)]
21pub struct FeatureGate {
22 pub name: String,
23 pub value: bool,
24 pub rule_id: String,
25 pub id_type: String,
26 pub details: EvaluationDetails,
27
28 pub(crate) __evaluation: Option<GateEvaluation>,
29 pub __version: Option<u32>,
30 pub __override_config_name: Option<String>,
31}
32
33#[derive(Serialize, Deserialize, Clone)]
34pub struct DynamicConfig {
35 pub name: String,
36 pub value: HashMap<String, Value>,
37 pub rule_id: String,
38 pub id_type: String,
39 pub details: EvaluationDetails,
40
41 pub __evaluation: Option<DynamicConfigEvaluation>,
42 pub __version: Option<u32>,
43 pub __override_config_name: Option<String>,
44}
45
46impl DynamicConfig {
47 #[must_use]
48 pub fn get_opt<T: DeserializeOwned>(&self, param_name: &str) -> Option<T> {
49 match self.value.get(param_name) {
50 Some(value) => from_value(value.clone()).ok(),
51 None => None,
52 }
53 }
54}
55
56#[derive(Serialize, Deserialize, Clone)]
57pub struct Experiment {
58 pub name: String,
59 pub value: HashMap<String, Value>,
60 pub rule_id: String,
61 pub id_type: String,
62 pub group_name: Option<String>,
63 pub details: EvaluationDetails,
64
65 pub __evaluation: Option<ExperimentEvaluation>,
66 pub __version: Option<u32>,
67 pub __override_config_name: Option<String>,
68}
69
70impl Experiment {
71 #[must_use]
72 pub fn get_opt<T: DeserializeOwned>(&self, param_name: &str) -> Option<T> {
73 match self.value.get(param_name) {
74 Some(value) => from_value(value.clone()).ok(),
75 None => None,
76 }
77 }
78}
79
80#[derive(Serialize, Deserialize, Clone)]
81pub struct Layer {
82 pub name: String,
83 pub rule_id: String,
84 pub id_type: String,
85
86 pub group_name: Option<String>,
87 pub details: EvaluationDetails,
88 pub allocated_experiment_name: Option<String>,
89
90 pub __evaluation: Option<LayerEvaluation>,
91 pub __value: HashMap<String, Value>,
92 pub __user: StatsigUserLoggable,
93 pub __version: Option<u32>,
94 pub __disable_exposure: bool,
95 pub __override_config_name: Option<String>,
96
97 #[serde(skip_serializing, skip_deserializing)]
98 pub __event_logger_ptr: Option<Weak<EventLogger>>,
99 #[serde(skip_serializing, skip_deserializing)]
100 pub __sampling_processor: Option<Weak<SamplingProcessor>>,
101}
102
103impl Layer {
104 pub fn get_opt<T: DeserializeOwned>(&self, param_name: &str) -> Option<T> {
105 let value = match self.__value.get(param_name) {
106 Some(value) => value.clone(),
107 None => return None,
108 };
109
110 match from_value(value.clone()) {
111 Ok(value) => {
112 self.log_param_exposure(param_name);
113 Some(value)
114 }
115 Err(_) => None,
116 }
117 }
118
119 pub fn get_raw_value(&self, param_name: &str) -> Option<Value> {
120 match self.__value.get(param_name) {
121 Some(value) => {
122 self.log_param_exposure(param_name);
123 Some(value.clone())
124 }
125 None => None,
126 }
127 }
128
129 fn log_param_exposure(&self, param_name: &str) -> Option<()> {
130 if self.__disable_exposure {
131 if let Some(ptr) = &self.__event_logger_ptr {
132 ptr.upgrade()?
133 .increment_non_exposure_checks_count(self.name.clone());
134 }
135 return None;
136 }
137
138 let mut sampling_details = SamplingDecision::default();
139
140 if let Some(ptr) = &self.__sampling_processor {
141 let layer_eval = self.__evaluation.as_ref();
142
143 sampling_details = ptr.upgrade()?.get_sampling_decision_and_details(
144 &self.__user.get_sampling_key(),
145 layer_eval.map(AnyEvaluation::from).as_ref(),
146 Some(param_name),
147 );
148
149 if !sampling_details.should_send_exposure {
150 return None;
151 }
152 }
153
154 if let Some(ptr) = &self.__event_logger_ptr {
155 ptr.upgrade()?
156 .enqueue(QueuedEventPayload::LayerExposure(LayerExposure {
157 user: self.__user.clone(),
158 layer_name: self.name.clone(),
159 parameter_name: param_name.to_string(),
160 evaluation: self.__evaluation.clone(),
161 evaluation_details: self.details.clone(),
162 version: self.__version,
163 is_manual_exposure: false,
164 sampling_details,
165 override_config_name: self.__override_config_name.clone(),
166 }));
167 }
168
169 None
170 }
171}
172
173macro_rules! impl_common_get_methods {
174 ($struct_name:ident) => {
175 impl $struct_name {
176 pub fn get<T: DeserializeOwned>(&self, param_name: &str, fallback: T) -> T {
177 self.get_opt(param_name).unwrap_or_else(|| fallback)
178 }
179
180 #[must_use]
181 pub fn get_bool(&self, param_name: &str, fallback: bool) -> bool {
182 self.get(param_name, fallback)
183 }
184
185 #[must_use]
186 pub fn get_f64(&self, param_name: &str, fallback: f64) -> f64 {
187 self.get(param_name, fallback)
188 }
189
190 #[must_use]
191 pub fn get_i64(&self, param_name: &str, fallback: i64) -> i64 {
192 self.get(param_name, fallback)
193 }
194
195 #[must_use]
196 pub fn get_string(&self, param_name: &str, fallback: String) -> String {
197 self.get(param_name, fallback)
198 }
199
200 #[must_use]
201 pub fn get_array(&self, param_name: &str, fallback: Vec<Value>) -> Vec<Value> {
202 self.get(param_name, fallback)
203 }
204
205 #[must_use]
206 pub fn get_object(
207 &self,
208 param_name: &str,
209 fallback: HashMap<String, Value>,
210 ) -> HashMap<String, Value> {
211 self.get(param_name, fallback)
212 }
213 }
214 };
215}
216
217#[derive(Serialize, Clone)]
218pub struct ParameterStore<'a> {
219 pub name: String,
220 pub details: EvaluationDetails,
221 pub parameters: HashMap<String, Parameter>,
222 pub options: ParameterStoreEvaluationOptions,
223
224 #[serde(skip_serializing, skip_deserializing)]
225 pub _statsig_ref: &'a Statsig,
226}
227
228impl ParameterStore<'_> {
229 pub fn get_opt<T: DeserializeOwned>(&self, user: &StatsigUser, param_name: &str) -> Option<T> {
230 let param = self.parameters.get(param_name)?;
231 match param {
232 Parameter::StaticValue(static_value) => from_value(static_value.value.clone()).ok(),
233 Parameter::Gate(gate) => {
234 let res = self._statsig_ref.check_gate_with_options(
235 user,
236 &gate.gate_name,
237 self.options.into(),
238 );
239 let val = match res {
240 true => gate.pass_value.clone(),
241 false => gate.fail_value.clone(),
242 };
243 from_value(val).ok()
244 }
245 Parameter::DynamicConfig(dynamic_config) => {
246 let res = self._statsig_ref.get_dynamic_config_with_options(
247 user,
248 &dynamic_config.config_name,
249 self.options.into(),
250 );
251 res.get_opt(&dynamic_config.param_name)?
252 }
253 Parameter::Experiment(experiment) => {
254 let res = self._statsig_ref.get_experiment_with_options(
255 user,
256 &experiment.experiment_name,
257 self.options.into(),
258 );
259 res.get_opt(&experiment.param_name)?
260 }
261 Parameter::Layer(layer) => {
262 let res = self._statsig_ref.get_layer_with_options(
263 user,
264 &layer.layer_name,
265 self.options.into(),
266 );
267 res.get_opt(&layer.param_name)?
268 }
269 }
270 }
271
272 pub fn get<T: DeserializeOwned>(&self, user: &StatsigUser, param_name: &str, fallback: T) -> T {
273 self.get_opt(user, param_name).unwrap_or(fallback)
274 }
275
276 pub fn get_json_value(
277 &self,
278 user: &StatsigUser,
279 param_name: &str,
280 fallback: Option<Value>,
281 ) -> Value {
282 match fallback {
283 None | Some(Value::Null) => self
284 .get_opt::<Value>(user, param_name)
285 .unwrap_or(Value::Null),
286 Some(Value::Bool(boolean)) => self.get_bool(user, param_name, boolean).into(),
287 Some(Value::Number(number)) => self.get(user, param_name, number).into(),
288 Some(Value::String(string)) => self.get_string(user, param_name, string).into(),
289 Some(Value::Array(vec)) => self.get_array(user, param_name, vec).into(),
290 Some(Value::Object(map)) => self
291 .get_object(user, param_name, map.into_iter().collect())
292 .into_iter()
293 .collect(),
294 }
295 }
296
297 pub fn get_bool(&self, user: &StatsigUser, param_name: &str, fallback: bool) -> bool {
298 self.get(user, param_name, fallback)
299 }
300
301 pub fn get_f64(&self, user: &StatsigUser, param_name: &str, fallback: f64) -> f64 {
302 self.get(user, param_name, fallback)
303 }
304
305 pub fn get_i64(&self, user: &StatsigUser, param_name: &str, fallback: i64) -> i64 {
306 self.get(user, param_name, fallback)
307 }
308
309 pub fn get_string(&self, user: &StatsigUser, param_name: &str, fallback: String) -> String {
310 self.get(user, param_name, fallback)
311 }
312
313 pub fn get_array(
314 &self,
315 user: &StatsigUser,
316 param_name: &str,
317 fallback: Vec<Value>,
318 ) -> Vec<Value> {
319 self.get(user, param_name, fallback)
320 }
321
322 pub fn get_object(
323 &self,
324 user: &StatsigUser,
325 param_name: &str,
326 fallback: HashMap<String, Value>,
327 ) -> HashMap<String, Value> {
328 self.get(user, param_name, fallback)
329 }
330}
331
332impl_common_get_methods!(DynamicConfig);
333impl_common_get_methods!(Experiment);
334impl_common_get_methods!(Layer);
335
336pub enum OverrideAdapterType {
337 LocalOverride,
338}