1use super::dynamic_returnable::DynamicReturnable;
2use super::evaluation_types::ExtraExposureInfo;
3use super::evaluation_types_v2::{
4 BaseEvaluationV2, DynamicConfigEvaluationV2, ExperimentEvaluationV2, GateEvaluationV2,
5 LayerEvaluationV2,
6};
7use crate::evaluation::evaluation_types::{
8 BaseEvaluation, DynamicConfigEvaluation, ExperimentEvaluation, GateEvaluation, LayerEvaluation,
9 SecondaryExposure,
10};
11use crate::event_logging::exposable_string::{self, ExposableString};
12use crate::hashing::{HashAlgorithm, HashUtil};
13use crate::specs_response::spec_types::Spec;
14
15#[derive(Default, Debug)]
16pub struct EvaluatorResult<'a> {
17 pub name: Option<&'a ExposableString>,
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 is_in_experiment: bool,
24 pub id_type: Option<&'a String>,
25 pub json_value: Option<DynamicReturnable>,
26 pub rule_id: Option<&'a ExposableString>,
27 pub rule_id_suffix: Option<&'static str>,
28 pub group_name: Option<&'a String>,
29 pub explicit_parameters: Option<&'a Vec<String>>,
30 pub config_delegate: Option<&'a String>,
31 pub secondary_exposures: Vec<SecondaryExposure>,
32 pub undelegated_secondary_exposures: Option<Vec<SecondaryExposure>>,
33 pub override_reason: Option<&'a str>,
34 pub version: Option<u32>,
35 pub sampling_rate: Option<u64>,
36 pub forward_all_exposures: Option<bool>,
37 pub override_config_name: Option<&'a str>,
38 pub has_seen_analytical_gates: Option<bool>,
39}
40
41pub fn result_to_gate_eval(gate_name: &str, result: &mut EvaluatorResult) -> GateEvaluation {
42 GateEvaluation {
43 base: result_to_base_eval(gate_name, result),
44 id_type: result.id_type.cloned().unwrap_or_default(),
45 value: result.bool_value,
46 }
47}
48
49pub fn result_to_gate_eval_v2(
50 gate_name: &str,
51 result: &mut EvaluatorResult,
52 hashing: &HashUtil,
53) -> GateEvaluationV2 {
54 GateEvaluationV2 {
55 base: result_to_base_eval_v2(gate_name, result, hashing),
56 id_type: result.id_type.cloned().unwrap_or_default(),
57 value: result.bool_value,
58 }
59}
60
61pub fn result_to_experiment_eval(
62 experiment_name: &str,
63 spec: Option<&Spec>,
64 result: &mut EvaluatorResult,
65) -> ExperimentEvaluation {
66 let (id_type, is_device_based) = get_id_type_info(result.id_type);
67
68 let mut is_experiment_active = None;
69 let mut is_user_in_experiment = None;
70
71 if spec.as_ref().is_none_or(|s| s.entity == "experiment") {
72 is_experiment_active = Some(result.is_experiment_active);
73 is_user_in_experiment = Some(result.is_experiment_group);
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
168 .rule_id
169 .map(|r| r.unperformant_to_string())
170 .unwrap_or_default(),
171 value: get_json_value(result),
172 is_device_based,
173 group_name: result.group_name.cloned(),
174 is_experiment_active,
175 is_user_in_experiment,
176 allocated_experiment_name,
177 explicit_parameters: result.explicit_parameters.cloned().unwrap_or_default(),
178 undelegated_secondary_exposures: Some(undelegated_sec_expos.unwrap_or_default()),
179 id_type,
180 }
181}
182
183pub fn result_to_layer_eval_v2(
184 layer_name: &str,
185 result: &mut EvaluatorResult,
186 hashing: &HashUtil,
187) -> LayerEvaluationV2 {
188 let mut undelegated_secondary_exposures = Vec::new();
189
190 if let Some(u) = &result.undelegated_secondary_exposures {
191 for exposure in u {
192 let key = format!(
193 "{}:{}:{}",
194 exposure.gate,
195 exposure.gate_value,
196 exposure.rule_id.as_str()
197 );
198 let hash = hashing.hash(&key, &HashAlgorithm::Djb2);
199 undelegated_secondary_exposures.push(hash.clone());
200 }
201 }
202
203 let mut allocated_experiment_name = None;
204 let mut is_experiment_active = None;
205 let mut is_user_in_experiment = None;
206
207 if let Some(config_delegate) = result.config_delegate {
208 if !config_delegate.is_empty() {
209 allocated_experiment_name = Some(config_delegate.clone());
210 is_experiment_active = Some(result.is_experiment_active);
211 is_user_in_experiment = Some(result.is_experiment_group);
212 }
213 }
214
215 let (id_type, is_device_based) = get_id_type_info(result.id_type);
216
217 LayerEvaluationV2 {
218 base: result_to_base_eval_v2(layer_name, result, hashing),
219 group: result.rule_id.cloned().unwrap_or_default(),
220 value: get_json_value(result),
221 is_device_based,
222 group_name: result.group_name.cloned(),
223 is_experiment_active,
224 is_user_in_experiment,
225 allocated_experiment_name,
226 explicit_parameters: result.explicit_parameters.cloned().unwrap_or_default(),
227 undelegated_secondary_exposures: Some(undelegated_secondary_exposures),
228 id_type,
229 }
230}
231
232pub fn result_to_dynamic_config_eval(
233 dynamic_config_name: &str,
234 result: &mut EvaluatorResult,
235) -> DynamicConfigEvaluation {
236 let (id_type, is_device_based) = get_id_type_info(result.id_type);
237
238 DynamicConfigEvaluation {
239 base: result_to_base_eval(dynamic_config_name, result),
240 id_type,
241 is_device_based,
242 value: get_json_value(result),
243 group: result.rule_id.cloned().unwrap_or_default(),
244 passed: result.bool_value,
245 }
246}
247
248pub fn result_to_dynamic_config_eval_v2(
249 dynamic_config_name: &str,
250 result: &mut EvaluatorResult,
251 hashing: &HashUtil,
252) -> DynamicConfigEvaluationV2 {
253 let (id_type, is_device_based) = get_id_type_info(result.id_type);
254
255 DynamicConfigEvaluationV2 {
256 base: result_to_base_eval_v2(dynamic_config_name, result, hashing),
257 id_type,
258 is_device_based,
259 value: get_json_value(result),
260 group: result.rule_id.cloned().unwrap_or_default(),
261 passed: result.bool_value,
262 }
263}
264
265fn get_id_type_info(id_type: Option<&String>) -> (String, bool) {
266 let id_type = id_type.cloned().unwrap_or_default();
267 let is_device_based = id_type == "stableID" || id_type == "stableid";
268 (id_type, is_device_based)
269}
270
271fn get_json_value(result: &mut EvaluatorResult) -> DynamicReturnable {
272 result
273 .json_value
274 .take()
275 .unwrap_or_else(DynamicReturnable::empty)
276}
277
278fn get_exposure_name_if_not_hashed(
280 possibly_hashed_name: &str,
281 exposure_name: Option<&ExposableString>,
282) -> ExposableString {
283 let exposure_name = exposure_name.unwrap_or(&exposable_string::EMPTY_STRING);
284 if possibly_hashed_name == exposure_name.as_str() {
285 exposure_name.clone()
286 } else {
287 ExposableString::from_str_ref(possibly_hashed_name)
288 }
289}
290
291fn result_to_base_eval(spec_name: &str, result: &mut EvaluatorResult) -> BaseEvaluation {
292 let rule_id = create_suffixed_rule_id(result.rule_id, result.rule_id_suffix);
293
294 let exposure_info = ExtraExposureInfo {
295 sampling_rate: result.sampling_rate,
296 forward_all_exposures: result.forward_all_exposures,
297 has_seen_analytical_gates: result.has_seen_analytical_gates,
298 override_config_name: result.override_config_name.map(|s| s.to_string()),
299 version: result.version,
300 };
301
302 let name = get_exposure_name_if_not_hashed(spec_name, result.name);
303
304 BaseEvaluation {
305 name,
306 rule_id,
307 secondary_exposures: std::mem::take(&mut result.secondary_exposures),
308 exposure_info: Some(exposure_info),
309 }
310}
311
312fn result_to_base_eval_v2(
313 spec_name: &str,
314 result: &mut EvaluatorResult,
315 hashing: &HashUtil,
316) -> BaseEvaluationV2 {
317 let mut exposures = Vec::new();
318
319 for exposure in &result.secondary_exposures {
320 let key = format!(
321 "{}:{}:{}",
322 exposure.gate,
323 exposure.gate_value,
324 exposure.rule_id.as_str()
325 );
326 let hash = hashing.hash(&key, &HashAlgorithm::Djb2);
327 exposures.push(hash.clone());
328 }
329
330 let rule_id = create_suffixed_rule_id(result.rule_id, result.rule_id_suffix);
331
332 BaseEvaluationV2 {
333 name: spec_name.to_string(),
334 rule_id,
335 secondary_exposures: exposures,
336 }
337}
338
339fn create_suffixed_rule_id(
340 rule_id: Option<&ExposableString>,
341 suffix: Option<&str>,
342) -> ExposableString {
343 let id_arc = match &rule_id {
344 Some(rule_id) => rule_id.as_str(),
345 None => "",
346 };
347
348 match &suffix {
349 Some(suffix) => ExposableString::from_str_parts(&[id_arc, ":", suffix]),
350 None => rule_id.cloned().unwrap_or_default(),
351 }
352}