statsig_rust/evaluation/
evaluation_types.rs

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