statsig_rust/evaluation/
evaluator_context.rs1use crate::evaluation::dynamic_value::DynamicValue;
2use crate::evaluation::evaluator_result::EvaluatorResult;
3use crate::hashing::HashUtil;
4use crate::spec_store::SpecStoreData;
5use crate::spec_types::{Rule, Spec};
6use crate::user::StatsigUserInternal;
7use crate::StatsigErr::StackOverflowError;
8use crate::{OverrideAdapter, StatsigErr};
9use std::collections::HashMap;
10use std::sync::Arc;
11
12const MAX_RECURSIVE_DEPTH: u16 = 300;
13
14pub struct EvaluatorContext<'a> {
15 pub user: &'a StatsigUserInternal<'a, 'a>,
16 pub spec_store_data: &'a SpecStoreData,
17 pub hashing: &'a HashUtil,
18 pub result: EvaluatorResult<'a>,
19 pub nested_count: u16,
20 pub app_id: &'a Option<&'a DynamicValue>,
21 pub override_adapter: &'a Option<Arc<dyn OverrideAdapter>>,
22 pub nested_gate_memo: HashMap<&'a str, (bool, Option<&'a String>)>,
23}
24
25impl<'a> EvaluatorContext<'a> {
26 pub fn new(
27 user: &'a StatsigUserInternal,
28 spec_store_data: &'a SpecStoreData,
29 hashing: &'a HashUtil,
30 app_id: &'a Option<&'a DynamicValue>,
31 override_adapter: &'a Option<Arc<dyn OverrideAdapter>>,
32 ) -> Self {
33 let result = EvaluatorResult::default();
34
35 Self {
36 user,
37 spec_store_data,
38 hashing,
39 app_id,
40 result,
41 override_adapter,
42 nested_count: 0,
43 nested_gate_memo: HashMap::new(),
44 }
45 }
46
47 pub fn reset_between_top_level_evaluations(&mut self) {
48 self.nested_gate_memo.clear();
49 self.nested_count = 0;
50 self.result = EvaluatorResult::default();
51 }
52
53 pub fn reset_result(&mut self) {
54 self.result = EvaluatorResult::default();
55 }
56
57 pub fn finalize_evaluation(&mut self, spec: &Spec, rule: Option<&Rule>) {
58 self.result.sampling_rate = rule.and_then(|r| r.sampling_rate);
59 self.result.forward_all_exposures = spec.forward_all_exposures;
60
61 if self.nested_count > 0 {
62 self.nested_count -= 1;
63 return;
64 }
65
66 if self.result.secondary_exposures.is_empty() {
67 return;
68 }
69
70 if self.result.undelegated_secondary_exposures.is_some() {
71 return;
72 }
73
74 self.result.undelegated_secondary_exposures = Some(self.result.secondary_exposures.clone());
75 }
76
77 pub fn prep_for_nested_evaluation(&mut self) -> Result<(), StatsigErr> {
78 self.nested_count += 1;
79
80 self.result.bool_value = false;
81 self.result.json_value = None;
82
83 if self.nested_count > MAX_RECURSIVE_DEPTH {
84 return Err(StackOverflowError);
85 }
86
87 Ok(())
88 }
89}