statsig_rust/evaluation/
evaluator_context.rs1use 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 pub use_experimental_ua_parser: bool,
25}
26
27impl<'a> EvaluatorContext<'a> {
28 pub fn new(
29 user: &'a StatsigUserInternal,
30 spec_store_data: &'a SpecStoreData,
31 hashing: &'a HashUtil,
32 app_id: Option<&'a DynamicValue>,
33 override_adapter: Option<&'a Arc<dyn OverrideAdapter>>,
34 use_experimental_ua_parser: bool,
35 ) -> Self {
36 let result = EvaluatorResult::default();
37
38 Self {
39 user,
40 spec_store_data,
41 hashing,
42 app_id,
43 result,
44 override_adapter,
45 nested_count: 0,
46 nested_gate_memo: HashMap::new(),
47 use_experimental_ua_parser,
48 }
49 }
50
51 pub fn reset_result(&mut self) {
52 self.nested_count = 0;
53 self.result = EvaluatorResult::default();
54 }
55
56 pub fn finalize_evaluation(&mut self, spec: &Spec, rule: Option<&Rule>) {
57 self.result.sampling_rate = rule.and_then(|r| r.sampling_rate);
58 self.result.forward_all_exposures = spec.forward_all_exposures;
59
60 if self.nested_count > 0 {
61 self.nested_count -= 1;
62 return;
63 }
64
65 if self.result.secondary_exposures.is_empty() {
66 return;
67 }
68
69 if self.result.undelegated_secondary_exposures.is_some() {
70 return;
71 }
72
73 self.result.undelegated_secondary_exposures = Some(self.result.secondary_exposures.clone());
74 }
75
76 pub fn prep_for_nested_evaluation(&mut self) -> Result<(), StatsigErr> {
77 self.nested_count += 1;
78
79 self.result.bool_value = false;
80 self.result.json_value = None;
81
82 if self.nested_count > MAX_RECURSIVE_DEPTH {
83 return Err(StackOverflowError);
84 }
85
86 Ok(())
87 }
88}