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