oai-statsig-rust 0.27.0

Statsig Rust SDK for usage in multi-user server environments.
Documentation
use std::collections::HashMap;

use ahash::AHashSet;
use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};

use crate::{
    evaluation::evaluator_value::{EvaluatorValueType, MemoizedEvaluatorValue},
    StatsigErr,
};

#[derive(Archive, RkyvDeserialize, RkyvSerialize)]
#[rkyv(bytecheck(bounds(__C: rkyv::validation::ArchiveContext)))]
pub(crate) struct MmapEvaluatorValue {
    pub(crate) value_type: MmapEvaluatorValueType,
    pub(crate) bool_value: Option<bool>,
    pub(crate) float_value: Option<f64>,
    pub(crate) string_value: Option<u64>,
    pub(crate) regex_value: Option<u64>,
    pub(crate) timestamp_value: Option<i64>,
    pub(crate) object_value: Option<HashMap<u64, u64>>,
    pub(crate) array_value: Option<HashMap<u64, (u32, u64)>>,
}

impl MmapEvaluatorValue {
    pub(crate) fn from_owned(value: &MemoizedEvaluatorValue) -> Result<Self, StatsigErr> {
        let array_value = value
            .array_value
            .as_ref()
            .map(|array| {
                array
                    .iter()
                    .map(|(lowercase, (index, original))| {
                        let index = u32::try_from(*index).map_err(|_| {
                            StatsigErr::SerializationError(
                                "Evaluator array index exceeds mmap format limit".to_string(),
                            )
                        })?;
                        Ok((lowercase.hash, (index, original.hash)))
                    })
                    .collect::<Result<HashMap<_, _>, StatsigErr>>()
            })
            .transpose()?;

        Ok(Self {
            value_type: value.value_type.into(),
            bool_value: value.bool_value,
            float_value: value.float_value,
            string_value: value.string_value.as_ref().map(|value| value.value.hash),
            regex_value: value
                .regex_value
                .as_ref()
                .and_then(|_| value.string_value.as_ref().map(|value| value.value.hash)),
            timestamp_value: value.timestamp_value,
            object_value: value.object_value.as_ref().map(|object| {
                object
                    .iter()
                    .map(|(key, value)| (key.hash, value.value.hash))
                    .collect()
            }),
            array_value,
        })
    }

    pub(crate) fn collect_string_hashes(&self, hashes: &mut AHashSet<u64>) {
        hashes.extend(self.string_value);
        hashes.extend(self.regex_value);
        if let Some(object) = &self.object_value {
            for (key, value) in object {
                hashes.insert(*key);
                hashes.insert(*value);
            }
        }
        if let Some(array) = &self.array_value {
            for (key, (_, value)) in array {
                hashes.insert(*key);
                hashes.insert(*value);
            }
        }
    }
}

#[derive(Archive, RkyvDeserialize, RkyvSerialize, Clone, Copy)]
#[rkyv(bytecheck(bounds(__C: rkyv::validation::ArchiveContext)))]
pub(crate) enum MmapEvaluatorValueType {
    Null,
    Bool,
    Number,
    String,
    Array,
    Object,
}

impl From<EvaluatorValueType> for MmapEvaluatorValueType {
    fn from(value: EvaluatorValueType) -> Self {
        match value {
            EvaluatorValueType::Null => Self::Null,
            EvaluatorValueType::Bool => Self::Bool,
            EvaluatorValueType::Number => Self::Number,
            EvaluatorValueType::String => Self::String,
            EvaluatorValueType::Array => Self::Array,
            EvaluatorValueType::Object => Self::Object,
        }
    }
}