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