statsig_rust/evaluation/
evaluation_types.rs

1use 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, // return false for all other types
86        }
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}