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