statsig_rust/evaluation/
evaluator_context.rs

1use 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}