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::interned_string::InternedString;
8use crate::spec_store::SpecStoreData;
9use crate::specs_response::spec_types::{Rule, Spec};
10use crate::user::StatsigUserInternal;
11use crate::StatsigErr::StackOverflowError;
12use crate::{OverrideAdapter, StatsigErr};
13
14const MAX_RECURSIVE_DEPTH: u16 = 300;
15
16pub struct EvaluatorContext<'a> {
17    pub user: &'a StatsigUserInternal<'a, 'a>,
18    pub spec_store_data: &'a SpecStoreData,
19    pub hashing: &'a HashUtil,
20    pub result: EvaluatorResult<'a>,
21    pub nested_count: u16,
22    pub app_id: Option<&'a DynamicValue>,
23    pub override_adapter: Option<&'a Arc<dyn OverrideAdapter>>,
24    pub nested_gate_memo: HashMap<&'a str, (bool, Option<&'a InternedString>)>,
25    pub should_user_third_party_parser: bool,
26}
27
28impl<'a> EvaluatorContext<'a> {
29    pub fn new(
30        user: &'a StatsigUserInternal,
31        spec_store_data: &'a SpecStoreData,
32        hashing: &'a HashUtil,
33        app_id: Option<&'a DynamicValue>,
34        override_adapter: Option<&'a Arc<dyn OverrideAdapter>>,
35        should_user_third_party_parser: bool,
36    ) -> Self {
37        let result = EvaluatorResult::default();
38
39        Self {
40            user,
41            spec_store_data,
42            hashing,
43            app_id,
44            result,
45            override_adapter,
46            nested_count: 0,
47            nested_gate_memo: HashMap::new(),
48            should_user_third_party_parser,
49        }
50    }
51
52    pub fn reset_result(&mut self) {
53        self.nested_count = 0;
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}