1use crate::evaluation::evaluation_types::{
2 BaseEvaluation, DynamicConfigEvaluation, ExperimentEvaluation, ExposureSamplingInfo,
3 GateEvaluation, LayerEvaluation, SecondaryExposure,
4};
5use crate::hashing::{HashAlgorithm, HashUtil};
6use crate::specs_response::spec_types::Spec;
7use serde_json::Value;
8use std::collections::HashMap;
9
10use super::evaluation_types_v2::{
11 BaseEvaluationV2, DynamicConfigEvaluationV2, ExperimentEvaluationV2, GateEvaluationV2,
12 LayerEvaluationV2,
13};
14
15#[derive(Default, Debug)]
16pub struct EvaluatorResult<'a> {
17 pub bool_value: bool,
18 pub unsupported: bool,
19 pub is_experiment_group: bool,
20 pub is_experiment_active: bool,
21 pub is_in_layer: bool,
22 pub id_type: Option<&'a String>,
23 pub json_value: Option<HashMap<String, Value>>,
24 pub rule_id: Option<&'a String>,
25 pub rule_id_suffix: Option<String>,
26 pub group_name: Option<&'a String>,
27 pub explicit_parameters: Option<&'a Vec<String>>,
28 pub config_delegate: Option<&'a String>,
29 pub secondary_exposures: Vec<SecondaryExposure>,
30 pub undelegated_secondary_exposures: Option<Vec<SecondaryExposure>>,
31 pub override_reason: Option<&'a str>,
32 pub version: Option<u32>,
33 pub sampling_rate: Option<u64>,
34 pub forward_all_exposures: Option<bool>,
35 pub override_config_name: Option<String>,
36 pub has_seen_analytical_gates: Option<bool>,
37}
38
39pub fn result_to_gate_eval(gate_name: &str, result: &mut EvaluatorResult) -> GateEvaluation {
40 GateEvaluation {
41 base: result_to_base_eval(gate_name, result),
42 id_type: result.id_type.cloned().unwrap_or_default(),
43 value: result.bool_value,
44 }
45}
46
47pub fn result_to_gate_eval_v2(
48 gate_name: &str,
49 result: &mut EvaluatorResult,
50 hashing: &HashUtil,
51) -> GateEvaluationV2 {
52 GateEvaluationV2 {
53 base: result_to_base_eval_v2(gate_name, result, hashing),
54 id_type: result.id_type.cloned().unwrap_or_default(),
55 value: result.bool_value,
56 }
57}
58
59pub fn result_to_experiment_eval(
60 experiment_name: &str,
61 spec: Option<&Spec>,
62 result: &mut EvaluatorResult,
63) -> ExperimentEvaluation {
64 let (id_type, is_device_based) = get_id_type_info(result.id_type);
65
66 let mut is_experiment_active = None;
67 let mut is_user_in_experiment = None;
68
69 if let Some(spec) = spec {
70 if spec.entity == "experiment" {
71 is_experiment_active = Some(result.is_experiment_active);
72 is_user_in_experiment = Some(result.is_experiment_group);
73 }
74 }
75
76 ExperimentEvaluation {
77 base: result_to_base_eval(experiment_name, result),
78 id_type,
79 group: result.rule_id.cloned().unwrap_or_default(),
80 is_device_based,
81 value: get_json_value(result),
82 is_in_layer: result.is_in_layer,
83 group_name: result.group_name.cloned(),
84 explicit_parameters: result.explicit_parameters.cloned(),
85 is_experiment_active,
86 is_user_in_experiment,
87 undelegated_secondary_exposures: std::mem::take(
88 &mut result.undelegated_secondary_exposures,
89 ),
90 }
91}
92
93pub fn result_to_experiment_eval_v2(
94 experiment_name: &str,
95 spec: Option<&Spec>,
96 result: &mut EvaluatorResult,
97 hashing: &HashUtil,
98) -> ExperimentEvaluationV2 {
99 let (id_type, is_device_based) = get_id_type_info(result.id_type);
100
101 let mut is_experiment_active = None;
102 let mut is_user_in_experiment = None;
103
104 if let Some(spec) = spec {
105 if spec.entity == "experiment" {
106 is_experiment_active = Some(result.is_experiment_active);
107 is_user_in_experiment = Some(result.is_experiment_group);
108 }
109 }
110
111 ExperimentEvaluationV2 {
112 base: result_to_base_eval_v2(experiment_name, result, hashing),
113 id_type,
114 group: result.rule_id.cloned().unwrap_or_default(),
115 is_device_based,
116 value: get_json_value(result),
117 is_in_layer: result.is_in_layer,
118 group_name: result.group_name.cloned(),
119 explicit_parameters: result.explicit_parameters.cloned(),
120 is_experiment_active,
121 is_user_in_experiment,
122 undelegated_secondary_exposures: result.undelegated_secondary_exposures.clone(),
123 }
124}
125
126pub fn eval_result_to_experiment_eval(
127 experiment_name: &str,
128 result: &mut EvaluatorResult,
129) -> ExperimentEvaluation {
130 let (id_type, is_device_based) = get_id_type_info(result.id_type);
131
132 ExperimentEvaluation {
133 base: result_to_base_eval(experiment_name, result),
134 id_type,
135 group: result.rule_id.cloned().unwrap_or_default(),
136 is_device_based,
137 value: get_json_value(result),
138 is_in_layer: result.is_in_layer,
139 group_name: result.group_name.cloned(),
140 explicit_parameters: result.explicit_parameters.cloned(),
141 is_experiment_active: Some(result.is_experiment_active),
142 is_user_in_experiment: Some(result.is_experiment_group),
143 undelegated_secondary_exposures: std::mem::take(
144 &mut result.undelegated_secondary_exposures,
145 ),
146 }
147}
148
149pub fn result_to_layer_eval(layer_name: &str, result: &mut EvaluatorResult) -> LayerEvaluation {
150 let mut allocated_experiment_name = None;
151 let mut is_experiment_active = None;
152 let mut is_user_in_experiment = None;
153
154 if let Some(config_delegate) = result.config_delegate {
155 if !config_delegate.is_empty() {
156 allocated_experiment_name = Some(config_delegate.clone());
157 is_experiment_active = Some(result.is_experiment_active);
158 is_user_in_experiment = Some(result.is_experiment_group);
159 }
160 }
161
162 let (id_type, is_device_based) = get_id_type_info(result.id_type);
163 let undelegated_sec_expos = std::mem::take(&mut result.undelegated_secondary_exposures);
164
165 LayerEvaluation {
166 base: result_to_base_eval(layer_name, result),
167 group: result.rule_id.cloned().unwrap_or_default(),
168 value: get_json_value(result),
169 is_device_based,
170 group_name: result.group_name.cloned(),
171 is_experiment_active,
172 is_user_in_experiment,
173 allocated_experiment_name,
174 explicit_parameters: result.explicit_parameters.cloned().unwrap_or_default(),
175 undelegated_secondary_exposures: Some(undelegated_sec_expos.unwrap_or_default()),
176 id_type,
177 }
178}
179
180pub fn result_to_layer_eval_v2(
181 layer_name: &str,
182 result: &mut EvaluatorResult,
183 hashing: &HashUtil,
184) -> LayerEvaluationV2 {
185 let mut undelegated_secondary_exposures = Vec::new();
186
187 if let Some(u) = &result.undelegated_secondary_exposures {
188 for exposure in u {
189 let key = format!(
190 "{}:{}:{}",
191 exposure.gate, exposure.gate_value, exposure.rule_id
192 );
193 let hash = hashing.hash(&key, &HashAlgorithm::Djb2);
194 undelegated_secondary_exposures.push(hash.clone());
195 }
196 }
197
198 let mut allocated_experiment_name = None;
199 let mut is_experiment_active = None;
200 let mut is_user_in_experiment = None;
201
202 if let Some(config_delegate) = result.config_delegate {
203 if !config_delegate.is_empty() {
204 allocated_experiment_name = Some(config_delegate.clone());
205 is_experiment_active = Some(result.is_experiment_active);
206 is_user_in_experiment = Some(result.is_experiment_group);
207 }
208 }
209
210 let (id_type, is_device_based) = get_id_type_info(result.id_type);
211
212 LayerEvaluationV2 {
213 base: result_to_base_eval_v2(layer_name, result, hashing),
214 group: result.rule_id.cloned().unwrap_or_default(),
215 value: get_json_value(result),
216 is_device_based,
217 group_name: result.group_name.cloned(),
218 is_experiment_active,
219 is_user_in_experiment,
220 allocated_experiment_name,
221 explicit_parameters: result.explicit_parameters.cloned().unwrap_or_default(),
222 undelegated_secondary_exposures: Some(undelegated_secondary_exposures),
223 id_type,
224 }
225}
226
227pub fn result_to_dynamic_config_eval(
228 dynamic_config_name: &str,
229 result: &mut EvaluatorResult,
230) -> DynamicConfigEvaluation {
231 let (id_type, is_device_based) = get_id_type_info(result.id_type);
232
233 DynamicConfigEvaluation {
234 base: result_to_base_eval(dynamic_config_name, result),
235 id_type,
236 is_device_based,
237 value: get_json_value(result),
238 group: result.rule_id.cloned().unwrap_or_default(),
239 passed: result.bool_value,
240 }
241}
242
243pub fn result_to_dynamic_config_eval_v2(
244 dynamic_config_name: &str,
245 result: &mut EvaluatorResult,
246 hashing: &HashUtil,
247) -> DynamicConfigEvaluationV2 {
248 let (id_type, is_device_based) = get_id_type_info(result.id_type);
249
250 DynamicConfigEvaluationV2 {
251 base: result_to_base_eval_v2(dynamic_config_name, result, hashing),
252 id_type,
253 is_device_based,
254 value: get_json_value(result),
255 group: result.rule_id.cloned().unwrap_or_default(),
256 passed: result.bool_value,
257 }
258}
259
260fn get_id_type_info(id_type: Option<&String>) -> (String, bool) {
261 let id_type = id_type.cloned().unwrap_or_default();
262 let is_device_based = id_type == "stableID" || id_type == "stableid";
263 (id_type, is_device_based)
264}
265
266fn get_json_value(result: &EvaluatorResult) -> HashMap<String, Value> {
267 result.json_value.clone().unwrap_or_default()
268}
269
270fn result_to_base_eval(spec_name: &str, result: &mut EvaluatorResult) -> BaseEvaluation {
271 let rule_id = match result.rule_id {
272 Some(rule_id) => rule_id.clone(),
273 None => String::new(),
274 };
275
276 let result_rule_id = match &result.rule_id_suffix {
277 Some(suffix) => format!("{rule_id}:{suffix}"),
278 None => rule_id.clone(),
279 };
280
281 let sampling_info = ExposureSamplingInfo {
282 sampling_rate: result.sampling_rate,
283 forward_all_exposures: result.forward_all_exposures,
284 has_seen_analytical_gates: result.has_seen_analytical_gates,
285 };
286
287 BaseEvaluation {
288 name: spec_name.to_string(),
289 rule_id: result_rule_id.clone(),
290 secondary_exposures: std::mem::take(&mut result.secondary_exposures),
291 sampling_info: Some(sampling_info),
292 }
293}
294
295fn result_to_base_eval_v2(
296 spec_name: &str,
297 result: &mut EvaluatorResult,
298 hashing: &HashUtil,
299) -> BaseEvaluationV2 {
300 let mut exposures = Vec::new();
301
302 for exposure in &result.secondary_exposures {
303 let key = format!(
304 "{}:{}:{}",
305 exposure.gate, exposure.gate_value, exposure.rule_id
306 );
307 let hash = hashing.hash(&key, &HashAlgorithm::Djb2);
308 exposures.push(hash.clone());
309 }
310
311 let rule_id = match result.rule_id {
312 Some(rule_id) => rule_id.clone(),
313 None => String::new(),
314 };
315
316 let result_rule_id = match &result.rule_id_suffix {
317 Some(suffix) => format!("{rule_id}:{suffix}"),
318 None => rule_id.clone(),
319 };
320
321 BaseEvaluationV2 {
322 name: spec_name.to_string(),
323 rule_id: result_rule_id.clone(),
324 secondary_exposures: exposures,
325 }
326}