statsig_rust/evaluation/
evaluation_types.rs

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