use std::collections::HashMap;
use std::sync::Arc;
use crate::evaluation::dynamic_value::DynamicValue;
use crate::evaluation::evaluator_result::EvaluatorResult;
use crate::hashing::HashUtil;
use crate::id_lists_adapter::IdList;
use crate::interned_string::InternedString;
use crate::specs_response::spec_types::{Rule, Spec, SpecsResponseFull};
use crate::user::StatsigUserInternal;
use crate::StatsigErr::StackOverflowError;
use crate::{OverrideAdapter, SecondaryExposure, Statsig, StatsigErr};
const MAX_RECURSIVE_DEPTH: u16 = 300;
type NestedGateMemo =
HashMap<InternedString, (bool, Option<InternedString>, Vec<SecondaryExposure>)>;
pub enum IdListResolution<'a> {
MapLookup(&'a HashMap<String, IdList>),
Callback(&'a dyn Fn(&str, &str) -> bool),
}
pub struct EvaluatorContext<'a> {
pub user: &'a StatsigUserInternal<'a, 'a>,
pub specs_data: &'a SpecsResponseFull,
pub id_list_resolver: IdListResolution<'a>,
pub hashing: &'a HashUtil,
pub result: EvaluatorResult,
pub nested_count: u16,
pub app_id: Option<&'a DynamicValue>,
pub override_adapter: Option<&'a Arc<dyn OverrideAdapter>>,
pub nested_gate_memo: NestedGateMemo,
pub should_user_third_party_parser: bool,
pub statsig: Option<&'a Statsig>,
pub disable_exposure_logging: bool,
pub gcir_hashes: Vec<u64>,
}
impl<'a> EvaluatorContext<'a> {
#[allow(clippy::too_many_arguments)]
pub fn new(
user: &'a StatsigUserInternal,
specs_data: &'a SpecsResponseFull,
id_list_resolver: IdListResolution<'a>,
hashing: &'a HashUtil,
app_id: Option<&'a DynamicValue>,
override_adapter: Option<&'a Arc<dyn OverrideAdapter>>,
should_user_third_party_parser: bool,
statsig: Option<&'a Statsig>,
disable_exposure_logging: bool,
) -> Self {
let result = EvaluatorResult::default();
Self {
user,
specs_data,
id_list_resolver,
hashing,
app_id,
result,
override_adapter,
nested_count: 0,
nested_gate_memo: HashMap::new(),
should_user_third_party_parser,
statsig,
disable_exposure_logging,
gcir_hashes: Vec::new(),
}
}
pub fn reset_result(&mut self) {
self.nested_count = 0;
self.result = EvaluatorResult::default();
}
pub fn finalize_evaluation(&mut self, spec: &Spec, rule: Option<&Rule>) {
self.result.sampling_rate = rule.and_then(|r| r.sampling_rate);
self.result.forward_all_exposures = spec.forward_all_exposures;
if self.nested_count > 0 {
self.nested_count -= 1;
return;
}
if self.result.secondary_exposures.is_empty() {
return;
}
if self.result.undelegated_secondary_exposures.is_some() {
return;
}
self.result.undelegated_secondary_exposures = Some(self.result.secondary_exposures.clone());
}
pub fn prep_for_nested_evaluation(&mut self) -> Result<(), StatsigErr> {
self.nested_count += 1;
self.result.bool_value = false;
self.result.json_value = None;
if self.nested_count > MAX_RECURSIVE_DEPTH {
return Err(StackOverflowError);
}
Ok(())
}
}