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::hashing::{HashAlgorithm, HashUtil};
12use crate::interned_string::InternedString;
13
14#[derive(Default, Debug)]
15pub struct EvaluatorResult<'a> {
16 pub name: Option<&'a InternedString>,
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 is_in_experiment: bool,
23 pub id_type: Option<InternedString>,
24 pub json_value: Option<DynamicReturnable>,
25 pub rule_id: Option<&'a InternedString>,
26 pub rule_id_suffix: Option<&'static str>,
27 pub group_name: Option<InternedString>,
28 pub explicit_parameters: Option<&'a Vec<InternedString>>,
29 pub config_delegate: Option<InternedString>,
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.take(),
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.take(),
56 value: result.bool_value,
57 }
58}
59
60pub fn result_to_experiment_eval(
61 experiment_name: &str,
62 spec_entity: Option<&str>,
63 result: &mut EvaluatorResult,
64) -> ExperimentEvaluation {
65 let (id_type, is_device_based) = get_id_type_info(result.id_type.as_ref());
66
67 let mut is_experiment_active = None;
68 let mut is_user_in_experiment = None;
69
70 if spec_entity.is_none_or(|s| s == "experiment") {
71 is_experiment_active = Some(result.is_experiment_active);
72 is_user_in_experiment = Some(result.is_experiment_group);
73 }
74
75 ExperimentEvaluation {
76 base: result_to_base_eval(experiment_name, result),
77 id_type: Some(id_type),
78 is_device_based,
79 value: get_json_value(result),
80 is_in_layer: result.is_in_layer,
81 group_name: result.group_name.take(),
82 explicit_parameters: result.explicit_parameters.cloned(),
83 is_experiment_active,
84 is_user_in_experiment,
85 undelegated_secondary_exposures: std::mem::take(
86 &mut result.undelegated_secondary_exposures,
87 ),
88 }
89}
90
91pub fn result_to_experiment_eval_v2(
92 experiment_name: &str,
93 spec_entity: Option<&str>,
94 result: &mut EvaluatorResult,
95 hashing: &HashUtil,
96) -> ExperimentEvaluationV2 {
97 let (id_type, is_device_based) = get_id_type_info(result.id_type.as_ref());
98
99 let mut is_experiment_active = None;
100 let mut is_user_in_experiment = None;
101
102 if let Some(spec_entity) = spec_entity {
103 if spec_entity == "experiment" {
104 is_experiment_active = Some(result.is_experiment_active);
105 is_user_in_experiment = Some(result.is_experiment_group);
106 }
107 }
108
109 ExperimentEvaluationV2 {
110 base: result_to_base_eval_v2(experiment_name, result, hashing),
111 id_type: Some(id_type),
112 is_device_based,
113 value: get_json_value(result),
114 is_in_layer: result.is_in_layer,
115 group_name: result.group_name.take(),
116 explicit_parameters: result.explicit_parameters.cloned(),
117 is_experiment_active,
118 is_user_in_experiment,
119 undelegated_secondary_exposures: result.undelegated_secondary_exposures.clone(),
120 }
121}
122
123pub fn eval_result_to_experiment_eval(
124 experiment_name: &str,
125 result: &mut EvaluatorResult,
126) -> ExperimentEvaluation {
127 let (id_type, is_device_based) = get_id_type_info(result.id_type.as_ref());
128
129 ExperimentEvaluation {
130 base: result_to_base_eval(experiment_name, result),
131 id_type: Some(id_type),
132 is_device_based,
133 value: get_json_value(result),
134 is_in_layer: result.is_in_layer,
135 group_name: result.group_name.take(),
136 explicit_parameters: result.explicit_parameters.cloned(),
137 is_experiment_active: Some(result.is_experiment_active),
138 is_user_in_experiment: Some(result.is_experiment_group),
139 undelegated_secondary_exposures: std::mem::take(
140 &mut result.undelegated_secondary_exposures,
141 ),
142 }
143}
144
145pub fn result_to_layer_eval(layer_name: &str, result: &mut EvaluatorResult) -> LayerEvaluation {
146 let mut allocated_experiment_name = None;
147 let mut is_experiment_active = None;
148 let mut is_user_in_experiment = None;
149
150 if let Some(config_delegate) = result.config_delegate.take() {
151 if !config_delegate.is_empty() {
152 allocated_experiment_name = Some(config_delegate.clone());
153 is_experiment_active = Some(result.is_experiment_active);
154 is_user_in_experiment = Some(result.is_experiment_group);
155 }
156 }
157
158 let (id_type, is_device_based) = get_id_type_info(result.id_type.as_ref());
159 let undelegated_sec_expos = std::mem::take(&mut result.undelegated_secondary_exposures);
160
161 LayerEvaluation {
162 base: result_to_base_eval(layer_name, result),
163 value: get_json_value(result),
164 is_device_based,
165 group_name: result.group_name.take(),
166 is_experiment_active,
167 is_user_in_experiment,
168 allocated_experiment_name,
169 explicit_parameters: result.explicit_parameters.cloned().unwrap_or_default(),
170 undelegated_secondary_exposures: Some(undelegated_sec_expos.unwrap_or_default()),
171 id_type: Some(id_type),
172 }
173}
174
175pub fn result_to_layer_eval_v2(
176 layer_name: &str,
177 result: &mut EvaluatorResult,
178 hashing: &HashUtil,
179) -> LayerEvaluationV2 {
180 let mut undelegated_secondary_exposures = Vec::new();
181
182 if let Some(u) = &result.undelegated_secondary_exposures {
183 for exposure in u {
184 let key = format!(
185 "{}:{}:{}",
186 exposure.gate,
187 exposure.gate_value,
188 exposure.rule_id.as_str()
189 );
190 let hash = hashing.hash(&key, &HashAlgorithm::Djb2);
191 undelegated_secondary_exposures.push(InternedString::from_string(hash));
192 }
193 }
194
195 let mut allocated_experiment_name = None;
196 let mut is_experiment_active = None;
197 let mut is_user_in_experiment = None;
198
199 if let Some(config_delegate) = result.config_delegate.take() {
200 if !config_delegate.is_empty() {
201 allocated_experiment_name = Some(config_delegate.clone());
202 is_experiment_active = Some(result.is_experiment_active);
203 is_user_in_experiment = Some(result.is_experiment_group);
204 }
205 }
206
207 let (id_type, is_device_based) = get_id_type_info(result.id_type.as_ref());
208
209 LayerEvaluationV2 {
210 base: result_to_base_eval_v2(layer_name, result, hashing),
211 value: get_json_value(result),
212 is_device_based,
213 group_name: result.group_name.take(),
214 is_experiment_active,
215 is_user_in_experiment,
216 allocated_experiment_name,
217 explicit_parameters: result.explicit_parameters.cloned().unwrap_or_default(),
218 undelegated_secondary_exposures: Some(undelegated_secondary_exposures),
219 id_type: Some(id_type),
220 }
221}
222
223pub fn result_to_dynamic_config_eval(
224 dynamic_config_name: &str,
225 result: &mut EvaluatorResult,
226) -> DynamicConfigEvaluation {
227 let (id_type, is_device_based) = get_id_type_info(result.id_type.as_ref());
228
229 DynamicConfigEvaluation {
230 base: result_to_base_eval(dynamic_config_name, result),
231 id_type: Some(id_type),
232 is_device_based,
233 value: get_json_value(result),
234 passed: result.bool_value,
235 }
236}
237
238pub fn result_to_dynamic_config_eval_v2(
239 dynamic_config_name: &str,
240 result: &mut EvaluatorResult,
241 hashing: &HashUtil,
242) -> DynamicConfigEvaluationV2 {
243 let (id_type, is_device_based) = get_id_type_info(result.id_type.as_ref());
244
245 DynamicConfigEvaluationV2 {
246 base: result_to_base_eval_v2(dynamic_config_name, result, hashing),
247 id_type: Some(id_type),
248 is_device_based,
249 value: get_json_value(result),
250 passed: result.bool_value,
251 }
252}
253
254fn get_id_type_info(id_type: Option<&InternedString>) -> (InternedString, bool) {
255 let id_type = id_type.cloned().unwrap_or_default();
256 let is_device_based = id_type == "stableID" || id_type == "stableid";
257 (id_type, is_device_based)
258}
259
260fn get_json_value(result: &mut EvaluatorResult) -> DynamicReturnable {
261 result
262 .json_value
263 .take()
264 .unwrap_or_else(DynamicReturnable::empty)
265}
266
267fn get_exposure_name_if_not_hashed(
269 possibly_hashed_name: &str,
270 exposure_name: Option<&InternedString>,
271) -> InternedString {
272 let exposure_name = exposure_name.unwrap_or(InternedString::empty_ref());
273 if possibly_hashed_name == exposure_name.as_str() {
274 exposure_name.clone()
275 } else {
276 InternedString::from_str_ref(possibly_hashed_name)
277 }
278}
279
280fn result_to_base_eval(spec_name: &str, result: &mut EvaluatorResult) -> BaseEvaluation {
281 let rule_id = create_suffixed_rule_id(result.rule_id, result.rule_id_suffix);
282
283 let exposure_info = ExtraExposureInfo {
284 sampling_rate: result.sampling_rate,
285 forward_all_exposures: result.forward_all_exposures,
286 has_seen_analytical_gates: result.has_seen_analytical_gates,
287 override_config_name: result.override_config_name.map(|s| s.to_string()),
288 version: result.version,
289 };
290
291 let name = get_exposure_name_if_not_hashed(spec_name, result.name);
292
293 BaseEvaluation {
294 name,
295 rule_id,
296 secondary_exposures: std::mem::take(&mut result.secondary_exposures),
297 exposure_info: Some(exposure_info),
298 }
299}
300
301fn result_to_base_eval_v2(
302 spec_name: &str,
303 result: &mut EvaluatorResult,
304 hashing: &HashUtil,
305) -> BaseEvaluationV2 {
306 let mut exposures = Vec::new();
307
308 for exposure in &result.secondary_exposures {
309 let key = format!(
310 "{}:{}:{}",
311 exposure.gate,
312 exposure.gate_value,
313 exposure.rule_id.as_str()
314 );
315 let hash = hashing.hash(&key, &HashAlgorithm::Djb2);
316 exposures.push(hash.clone());
317 }
318
319 let rule_id = create_suffixed_rule_id(result.rule_id, result.rule_id_suffix);
320
321 BaseEvaluationV2 {
322 name: spec_name.to_string(),
323 rule_id,
324 secondary_exposures: exposures,
325 }
326}
327
328fn create_suffixed_rule_id(
329 rule_id: Option<&InternedString>,
330 suffix: Option<&str>,
331) -> InternedString {
332 let id_arc = match &rule_id {
333 Some(rule_id) => rule_id.as_str(),
334 None => "",
335 };
336
337 match &suffix {
338 Some(suffix) => InternedString::from_str_parts(&[id_arc, ":", suffix]),
339 None => rule_id.cloned().unwrap_or_default(),
340 }
341}