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