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