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