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