statsig_rust/evaluation/
evaluator_context.rs

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