statsig_rust/evaluation/
evaluation_types.rs

1use super::dynamic_returnable::DynamicReturnable;
2use crate::event_logging::exposable_string::ExposableString;
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: String,
13    pub gate_value: String,
14    #[serde(rename = "ruleID")]
15    pub rule_id: ExposableString,
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: ExposableString,
42    pub rule_id: ExposableString,
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    pub id_type: String,
104    pub value: bool,
105}
106
107#[derive(Serialize, Deserialize, Clone)]
108pub struct DynamicConfigEvaluation {
109    #[serde(flatten)]
110    pub base: BaseEvaluation,
111
112    pub id_type: String,
113    pub value: DynamicReturnable,
114
115    // The 'group' field is identical to 'rule_id'. See group_name instead.
116    pub group: ExposableString,
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    pub id_type: String,
128    pub value: DynamicReturnable,
129
130    // The 'group' field is identical to 'rule_id'. See group_name instead.
131    pub group: ExposableString,
132    pub is_device_based: bool,
133
134    #[serde(skip_serializing_if = "is_false")]
135    pub is_in_layer: bool,
136
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub explicit_parameters: Option<Vec<String>>,
139
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub group_name: Option<String>,
142
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub is_experiment_active: Option<bool>,
145
146    #[serde(skip_serializing_if = "Option::is_none")]
147    pub is_user_in_experiment: Option<bool>,
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub undelegated_secondary_exposures: Option<Vec<SecondaryExposure>>,
150}
151
152#[derive(Serialize, Deserialize, Clone)]
153#[serde(untagged)]
154pub enum AnyConfigEvaluation {
155    DynamicConfig(DynamicConfigEvaluation),
156    Experiment(ExperimentEvaluation),
157}
158
159#[derive(Serialize, Deserialize, Clone)]
160pub struct LayerEvaluation {
161    #[serde(flatten)]
162    pub base: BaseEvaluation,
163
164    pub value: DynamicReturnable,
165
166    pub id_type: String,
167
168    // The 'group' field is identical to 'rule_id'. See group_name instead.
169    pub group: String,
170    pub is_device_based: bool,
171
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub group_name: Option<String>,
174
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub is_experiment_active: Option<bool>,
177
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub is_user_in_experiment: Option<bool>,
180
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub allocated_experiment_name: Option<String>,
183    pub explicit_parameters: Vec<String>,
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub undelegated_secondary_exposures: Option<Vec<SecondaryExposure>>,
186}