statsig_rust/evaluation/
evaluation_types.rs

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