growthbook_sdk_rust/namespace/
use_case.rs

1use crate::hash::{HashCode, HashCodeVersion};
2use crate::model_public::GrowthBookAttributeValue;
3use crate::range::model::Range;
4
5pub struct Namespace;
6
7impl Namespace {
8    pub fn is_in(
9        user_value: &GrowthBookAttributeValue,
10        namespace: &str,
11        range: &Range,
12    ) -> bool {
13        let user_weight = HashCode::hash_code(&format!("{}__", user_value), namespace, HashCodeVersion::from(1)).unwrap_or(-1.0);
14        range.in_range(&user_weight)
15    }
16}
17
18#[cfg(test)]
19mod test {
20    use std::fs;
21
22    use serde::Deserialize;
23    use serde_json::Value;
24
25    use crate::model_public::GrowthBookAttributeValue;
26    use crate::namespace::use_case::Namespace;
27    use crate::range::model::Range;
28
29    #[tokio::test]
30    async fn evaluate_get_bucket_range() -> Result<(), Box<dyn std::error::Error>> {
31        let cases = Cases::new();
32
33        for value in cases.in_namespace {
34            let eval_in_namespace = EvalInNamespace::new(value);
35            let range = Range {
36                start: eval_in_namespace.range_start,
37                end: eval_in_namespace.range_end,
38            };
39            let result = Namespace::is_in(&GrowthBookAttributeValue::String(eval_in_namespace.user_value.clone()), &eval_in_namespace.namespace_name, &range);
40            if result != eval_in_namespace.result {
41                panic!(
42                    "EvalInNamespace failed: name='{}' expected_result={} result={result}",
43                    eval_in_namespace.name, eval_in_namespace.user_value
44                )
45            }
46        }
47
48        Ok(())
49    }
50
51    #[derive(Deserialize, Clone)]
52    #[serde(rename_all = "camelCase")]
53    struct Cases {
54        in_namespace: Vec<Value>,
55    }
56
57    pub struct EvalInNamespace {
58        name: String,
59        user_value: String,
60        namespace_name: String,
61        range_start: f32,
62        range_end: f32,
63        result: bool,
64    }
65
66    impl EvalInNamespace {
67        fn new(value: Value) -> Self {
68            let array = value.as_array().expect("Failed to convert to array");
69            Self {
70                name: array[0].as_str().expect("Failed to convert do str").to_string(),
71                user_value: array[1].as_str().expect("Failed to convert do str").to_string(),
72                namespace_name: array[2].as_array().expect("Failed to convert to array")[0].as_str().expect("Failed to convert to str").to_string(),
73                range_start: array[2].as_array().expect("Failed to convert to array")[1].as_f64().expect("Failed to convert to str") as f32,
74                range_end: array[2].as_array().expect("Failed to convert to array")[2].as_f64().expect("Failed to convert to str") as f32,
75                result: array[3].as_bool().expect("Failed to convert do bool"),
76            }
77        }
78    }
79
80    impl Cases {
81        pub fn new() -> Self {
82            let contents = fs::read_to_string("./tests/all_cases.json").expect("Should have been able to read the file");
83
84            serde_json::from_str(&contents).expect("Failed to create cases")
85        }
86    }
87}