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}