statsig_rust/evaluation/
evaluation_types.rs1use std::collections::HashMap;
2
3use super::dynamic_returnable::DynamicReturnable;
4use crate::{
5 evaluation::secondary_exposure_key::SecondaryExposureKey, interned_string::InternedString,
6 specs_response::explicit_params::ExplicitParameters,
7};
8
9use serde::{Deserialize, Serialize};
10
11pub fn is_false(v: &bool) -> bool {
12 !(*v)
13}
14
15#[derive(Serialize, Deserialize, Clone, Debug)]
16#[serde(rename_all = "camelCase")]
17pub struct SecondaryExposure {
18 pub gate: InternedString,
19 pub gate_value: InternedString,
20 #[serde(rename = "ruleID")]
21 pub rule_id: InternedString,
22}
23
24impl SecondaryExposure {
25 pub fn get_dedupe_key(&self) -> String {
26 let mut key = String::new();
27 key += &self.gate;
28 key += "|";
29 key += &self.gate_value;
30 key += "|";
31 key += self.rule_id.as_str();
32 key
33 }
34}
35
36impl From<&SecondaryExposure> for SecondaryExposureKey {
37 fn from(val: &SecondaryExposure) -> Self {
38 SecondaryExposureKey {
39 gate_name_hash: val.gate.hash,
40 rule_id_hash: val.rule_id.hash,
41 gate_value_hash: val.gate_value.hash,
42 }
43 }
44}
45
46#[derive(Serialize, Deserialize, Clone, Debug, Default)]
47pub struct ExtraExposureInfo {
48 pub sampling_rate: Option<u64>,
49 pub forward_all_exposures: Option<bool>,
50 pub has_seen_analytical_gates: Option<bool>,
51 pub override_config_name: Option<InternedString>,
52 pub version: Option<u32>,
53}
54
55#[derive(Serialize, Deserialize, Clone)]
56pub struct BaseEvaluation {
57 pub name: InternedString,
58 pub rule_id: InternedString,
59 pub secondary_exposures: Vec<SecondaryExposure>,
60
61 #[serde(skip_serializing)]
62 pub(crate) exposure_info: Option<ExtraExposureInfo>,
63}
64
65pub enum AnyEvaluation<'a> {
66 FeatureGate(&'a GateEvaluation),
67 DynamicConfig(&'a DynamicConfigEvaluation),
68 Experiment(&'a ExperimentEvaluation),
69 Layer(&'a LayerEvaluation),
70}
71
72impl AnyEvaluation<'_> {
73 pub fn get_base_result(&self) -> &BaseEvaluation {
74 match self {
75 AnyEvaluation::FeatureGate(gate) => &gate.base,
76 AnyEvaluation::DynamicConfig(config) => &config.base,
77 AnyEvaluation::Experiment(experiment) => &experiment.base,
78 AnyEvaluation::Layer(layer) => &layer.base,
79 }
80 }
81
82 pub fn get_gate_bool_value(&self) -> bool {
83 match self {
84 AnyEvaluation::FeatureGate(eval) => eval.value,
85 _ => false, }
87 }
88}
89
90impl<'a> From<&'a LayerEvaluation> for AnyEvaluation<'a> {
91 fn from(layer_eval: &'a LayerEvaluation) -> Self {
92 AnyEvaluation::Layer(layer_eval)
93 }
94}
95
96impl<'a> From<&'a GateEvaluation> for AnyEvaluation<'a> {
97 fn from(gate_eval: &'a GateEvaluation) -> Self {
98 AnyEvaluation::FeatureGate(gate_eval)
99 }
100}
101
102impl<'a> From<&'a ExperimentEvaluation> for AnyEvaluation<'a> {
103 fn from(experiment_eval: &'a ExperimentEvaluation) -> Self {
104 AnyEvaluation::Experiment(experiment_eval)
105 }
106}
107
108impl<'a> From<&'a DynamicConfigEvaluation> for AnyEvaluation<'a> {
109 fn from(dynamic_config_evalation: &'a DynamicConfigEvaluation) -> Self {
110 AnyEvaluation::DynamicConfig(dynamic_config_evalation)
111 }
112}
113
114#[derive(Serialize, Deserialize, Clone)]
115pub struct GateEvaluation {
116 #[serde(flatten)]
117 pub base: BaseEvaluation,
118
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub id_type: Option<InternedString>,
121 pub value: bool,
122}
123
124#[derive(Serialize, Deserialize, Clone)]
125pub struct DynamicConfigEvaluation {
126 #[serde(flatten)]
127 pub base: BaseEvaluation,
128
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub id_type: Option<InternedString>,
131 pub value: DynamicReturnable,
132
133 pub is_device_based: bool,
134
135 pub passed: bool,
136}
137
138#[derive(Serialize, Deserialize, Clone)]
139pub struct ExperimentEvaluation {
140 #[serde(flatten)]
141 pub base: BaseEvaluation,
142
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub id_type: Option<InternedString>,
145 pub value: DynamicReturnable,
146
147 pub is_device_based: bool,
148
149 #[serde(skip_serializing_if = "is_false")]
150 pub is_in_layer: bool,
151
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub explicit_parameters: Option<ExplicitParameters>,
154
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub group_name: Option<InternedString>,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub is_experiment_active: Option<bool>,
160
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub is_user_in_experiment: Option<bool>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub undelegated_secondary_exposures: Option<Vec<SecondaryExposure>>,
165}
166
167#[derive(Serialize, Deserialize, Clone)]
168#[serde(untagged)]
169pub enum AnyConfigEvaluation {
170 DynamicConfig(DynamicConfigEvaluation),
171 Experiment(ExperimentEvaluation),
172}
173
174#[derive(Serialize, Deserialize, Clone)]
175pub struct LayerEvaluation {
176 #[serde(flatten)]
177 pub base: BaseEvaluation,
178
179 pub value: DynamicReturnable,
180
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub id_type: Option<InternedString>,
183
184 pub is_device_based: bool,
185
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub group_name: Option<InternedString>,
188
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub is_experiment_active: Option<bool>,
191
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub is_user_in_experiment: Option<bool>,
194
195 #[serde(skip_serializing_if = "Option::is_none")]
196 pub allocated_experiment_name: Option<InternedString>,
197 pub explicit_parameters: ExplicitParameters,
198 #[serde(skip_serializing_if = "Option::is_none")]
199 pub undelegated_secondary_exposures: Option<Vec<SecondaryExposure>>,
200
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub parameter_rule_ids: Option<HashMap<InternedString, InternedString>>,
203}