statsig_rust/
spec_types.rs

1use crate::evaluation::dynamic_returnable::DynamicReturnable;
2use crate::evaluation::dynamic_string::DynamicString;
3use crate::evaluation::dynamic_value::DynamicValue;
4use serde::{Deserialize, Deserializer, Serialize};
5use serde_json::Value;
6use std::collections::HashMap;
7
8#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
9#[serde(rename_all = "camelCase")]
10pub struct Spec {
11    #[serde(rename = "type")]
12    pub _type: String,
13    pub salt: String,
14    pub default_value: DynamicReturnable,
15    pub enabled: bool,
16    pub rules: Vec<Rule>,
17    pub id_type: String,
18    pub explicit_parameters: Option<Vec<String>>,
19    pub entity: String,
20    pub has_shared_params: Option<bool>,
21    pub is_active: Option<bool>,
22    pub version: Option<u32>,
23    #[serde(rename = "targetAppIDs")]
24    pub target_app_ids: Option<Vec<String>>,
25    pub forward_all_exposures: Option<bool>,
26    pub fields_used: Option<Vec<String>>,
27}
28
29#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
30#[serde(rename_all = "camelCase")]
31pub struct Rule {
32    pub name: String,
33    pub pass_percentage: f64,
34    pub return_value: DynamicReturnable,
35    pub id: String,
36    pub salt: Option<String>,
37    pub conditions: Vec<String>,
38    pub id_type: DynamicString,
39    pub group_name: Option<String>,
40    pub config_delegate: Option<String>,
41    pub is_experiment_group: Option<bool>,
42    pub sampling_rate: Option<u64>,
43}
44
45#[derive(Serialize, Clone, PartialEq, Debug)]
46#[serde(rename_all = "camelCase")]
47pub struct Condition {
48    #[serde(rename = "type")]
49    pub condition_type: String,
50    pub target_value: Option<DynamicValue>,
51    pub operator: Option<String>,
52    pub field: Option<DynamicString>,
53    pub additional_values: Option<HashMap<String, DynamicValue>>,
54    pub id_type: DynamicString,
55}
56
57#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
58pub struct OverrideRule {
59    pub rule_name: String,
60    pub start_time: Option<i64>,
61}
62
63#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
64pub struct ConfigMapping {
65    pub new_config_name: String,
66    pub rules: Vec<OverrideRule>,
67}
68
69#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
70pub struct SpecsResponseFull {
71    pub feature_gates: HashMap<String, Spec>,
72    pub dynamic_configs: HashMap<String, Spec>,
73    pub layer_configs: HashMap<String, Spec>,
74    pub condition_map: HashMap<String, Condition>,
75    pub experiment_to_layer: HashMap<String, String>,
76    pub has_updates: bool,
77    pub time: u64,
78    pub checksum: Option<String>,
79    pub default_environment: Option<String>,
80    pub app_id: Option<DynamicValue>,
81    pub sdk_keys_to_app_ids: Option<HashMap<String, DynamicValue>>,
82    pub hashed_sdk_keys_to_app_ids: Option<HashMap<String, DynamicValue>>,
83    pub diagnostics: Option<HashMap<String, f64>>,
84    pub param_stores: Option<HashMap<String, ParameterStore>>,
85    pub sdk_configs: Option<HashMap<String, DynamicValue>>,
86    pub cmab_configs: Option<HashMap<String, CMABConfig>>,
87    pub overrides: Option<HashMap<String, Vec<ConfigMapping>>>,
88    pub override_rules: Option<HashMap<String, Rule>>,
89}
90
91#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
92#[serde(rename_all = "camelCase")]
93pub struct CMABConfig {
94    pub salt: String,
95    #[serde(rename = "targetAppIDs")]
96    pub target_app_ids: Option<Vec<String>>,
97    pub default_value: DynamicReturnable,
98    pub id_type: DynamicString,
99    pub enabled: bool,
100    pub version: u32,
101    pub sample_rate: f64,
102    pub higher_is_better: bool,
103    pub groups: Vec<CMABGroup>,
104    pub config: Option<HashMap<String, CMABGroupConfig>>,
105    pub targeting_gate_name: Option<String>,
106}
107
108#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
109#[serde(rename_all = "camelCase")]
110pub struct CMABGroup {
111    pub name: String,
112    pub parameter_values: DynamicReturnable,
113    pub id: String,
114}
115
116#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
117#[serde(rename_all = "camelCase")]
118pub struct CMABGroupConfig {
119    pub alpha: f64,
120    pub intercept: f64,
121    pub records: u64,
122    pub weights_numerical: HashMap<String, f64>,
123    pub weights_categorical: HashMap<String, HashMap<String, f64>>,
124}
125
126#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
127#[serde(untagged)]
128pub enum Parameter {
129    StaticValue(StaticValueParameter),
130    Gate(GateParameter),
131    DynamicConfig(DynamicConfigParameter),
132    Experiment(ExperimentParameter),
133    Layer(LayerParameter),
134}
135
136#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
137pub struct ParameterStore {
138    pub parameters: HashMap<String, Parameter>,
139    #[serde(rename = "targetAppIDs")]
140    pub target_app_ids: Option<Vec<String>>,
141}
142
143#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
144pub struct StaticValueParameter {
145    pub ref_type: String,
146    pub param_type: String,
147    pub value: Value,
148}
149
150#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
151pub struct GateParameter {
152    pub ref_type: String,
153    pub param_type: String,
154    pub gate_name: String,
155    pub pass_value: Value,
156    pub fail_value: Value,
157}
158
159#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
160pub struct DynamicConfigParameter {
161    pub ref_type: String,
162    pub param_type: String,
163    pub config_name: String,
164    pub param_name: String,
165}
166
167#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
168pub struct ExperimentParameter {
169    pub ref_type: String,
170    pub param_type: String,
171    pub experiment_name: String,
172    pub param_name: String,
173}
174
175#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
176pub struct LayerParameter {
177    pub ref_type: String,
178    pub param_type: String,
179    pub layer_name: String,
180    pub param_name: String,
181}
182
183#[derive(Deserialize)]
184pub struct SpecsResponseNoUpdates {
185    pub has_updates: bool,
186}
187
188#[derive(Deserialize)]
189#[serde(untagged)]
190pub enum SpecsResponse {
191    Full(Box<SpecsResponseFull>),
192    NoUpdates(SpecsResponseNoUpdates),
193}
194
195impl<'de> Deserialize<'de> for Condition {
196    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
197    where
198        D: Deserializer<'de>,
199    {
200        #[derive(Deserialize)]
201        #[serde(rename_all = "camelCase")]
202        struct ConditionInternal {
203            #[serde(rename = "type")]
204            condition_type: String,
205            target_value: Option<DynamicValue>,
206            operator: Option<String>,
207            field: Option<DynamicString>,
208            additional_values: Option<HashMap<String, DynamicValue>>,
209            id_type: DynamicString,
210        }
211
212        let mut internal = ConditionInternal::deserialize(deserializer)?;
213
214        if let Some(ref op) = internal.operator {
215            if op == "str_matches" {
216                if let Some(ref mut tv) = internal.target_value {
217                    tv.compile_regex();
218                }
219            }
220        }
221
222        Ok(Condition {
223            condition_type: internal.condition_type,
224            target_value: internal.target_value,
225            operator: internal.operator,
226            field: internal.field,
227            additional_values: internal.additional_values,
228            id_type: internal.id_type,
229        })
230    }
231}