absmartly-sdk 0.1.0

ABsmartly SDK for Rust - A/B testing and feature flagging
Documentation
use crate::murmur3::murmur3_32;
use crate::utils::choose_variant;

pub struct VariantAssigner {
    unit_hash: u32,
}

impl VariantAssigner {
    pub fn new(unit: &str) -> Self {
        let unit_hash = murmur3_32(unit.as_bytes(), 0);
        Self { unit_hash }
    }

    pub fn assign(&self, split: &[f64], seed_hi: u32, seed_lo: u32) -> usize {
        let prob = self.probability(seed_hi, seed_lo);
        choose_variant(split, prob)
    }

    fn probability(&self, seed_hi: u32, seed_lo: u32) -> f64 {
        let mut buffer = [0u8; 12];
        buffer[0..4].copy_from_slice(&seed_lo.to_le_bytes());
        buffer[4..8].copy_from_slice(&seed_hi.to_le_bytes());
        buffer[8..12].copy_from_slice(&self.unit_hash.to_le_bytes());

        let hash = murmur3_32(&buffer, 0);
        (hash as f64) / (0xFFFFFFFFu32 as f64)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::utils::hash_unit;

    #[test]
    fn test_assigner_bleh_email() {
        let hashed_unit = hash_unit("bleh@absmartly.com");
        let assigner = VariantAssigner::new(&hashed_unit);
        let variant = assigner.assign(&[0.5, 0.5], 0x00000000, 0x00000000);
        assert_eq!(variant, 0);
    }

    #[test]
    fn test_assigner_bleh_email_different_seed() {
        let hashed_unit = hash_unit("bleh@absmartly.com");
        let assigner = VariantAssigner::new(&hashed_unit);
        let variant = assigner.assign(&[0.5, 0.5], 0x00000000, 0x00000001);
        assert_eq!(variant, 1);
    }

    #[test]
    fn test_assigner_123456789() {
        let hashed_unit = hash_unit("123456789");
        let assigner = VariantAssigner::new(&hashed_unit);
        assert_eq!(assigner.assign(&[0.5, 0.5], 0x00000000, 0x00000000), 1);
        assert_eq!(assigner.assign(&[0.5, 0.5], 0x00000000, 0x00000001), 0);
    }

    #[test]
    fn test_assigner_three_way_split() {
        let hashed_unit = hash_unit("bleh@absmartly.com");
        let assigner = VariantAssigner::new(&hashed_unit);
        let variant = assigner.assign(&[0.33, 0.33, 0.34], 0x00000000, 0x00000001);
        assert_eq!(variant, 2);
    }
}