Skip to main content

statsig_rust/evaluation/
dynamic_returnable.rs

1use std::{borrow::Cow, collections::HashMap, sync::Arc};
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use serde_json::{
5    value::{to_raw_value, RawValue},
6    Value as JsonValue,
7};
8
9use crate::{interned_values::InternedStore, log_e};
10
11const TAG: &str = "DynamicReturnable";
12
13lazy_static::lazy_static! {
14    static ref EMPTY_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
15        hash: 0,
16        value: DynamicReturnableValue::Null,
17    };
18
19    static ref TRUE_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
20        hash: 0,
21        value: DynamicReturnableValue::Bool(true),
22    };
23
24    static ref FALSE_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
25        hash: 0,
26        value: DynamicReturnableValue::Bool(false),
27    };
28
29}
30
31#[derive(Clone, PartialEq, Debug)]
32pub struct DynamicReturnable {
33    pub hash: u64,
34    pub value: DynamicReturnableValue,
35}
36
37impl DynamicReturnable {
38    pub fn empty() -> Self {
39        EMPTY_DYNAMIC_RETURNABLE.clone()
40    }
41
42    pub fn from_bool(value: bool) -> Self {
43        if value {
44            TRUE_DYNAMIC_RETURNABLE.clone()
45        } else {
46            FALSE_DYNAMIC_RETURNABLE.clone()
47        }
48    }
49
50    pub fn from_map(value: HashMap<String, JsonValue>) -> Self {
51        let raw_value = match to_raw_value(&value) {
52            Ok(raw_value) => raw_value,
53            Err(e) => {
54                log_e!(TAG, "Failed to convert map to raw value: {}", e);
55                return Self::empty();
56            }
57        };
58
59        InternedStore::get_or_intern_returnable(Cow::Owned(raw_value))
60    }
61
62    pub fn get_bool(&self) -> Option<bool> {
63        match self.value {
64            DynamicReturnableValue::Bool(value) => Some(value),
65            _ => None,
66        }
67    }
68
69    pub fn get_json(&self) -> Option<HashMap<String, JsonValue>> {
70        let bytes = match &self.value {
71            DynamicReturnableValue::JsonPointer(bytes) => bytes.get().as_bytes(),
72            DynamicReturnableValue::JsonStatic(bytes) => bytes.get().as_bytes(),
73            _ => return None,
74        };
75
76        match serde_json::from_slice(bytes) {
77            Ok(json) => Some(json),
78            Err(e) => {
79                log_e!(TAG, "Failed to parse json. Error: {}", e);
80                None
81            }
82        }
83    }
84
85    pub fn get_hash(&self) -> u64 {
86        self.hash
87    }
88}
89
90impl<'de> Deserialize<'de> for DynamicReturnable {
91    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
92    where
93        D: Deserializer<'de>,
94    {
95        let raw_value_ref: Cow<RawValue> = Deserialize::deserialize(deserializer)?;
96        Ok(InternedStore::get_or_intern_returnable(raw_value_ref))
97    }
98}
99
100impl Serialize for DynamicReturnable {
101    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
102    where
103        S: Serializer,
104    {
105        match &self.value {
106            DynamicReturnableValue::JsonPointer(raw) => raw.serialize(serializer),
107            DynamicReturnableValue::JsonStatic(raw) => raw.serialize(serializer),
108            DynamicReturnableValue::Null => serializer.serialize_none(),
109            DynamicReturnableValue::Bool(value) => serializer.serialize_bool(*value),
110        }
111    }
112}
113
114impl Drop for DynamicReturnable {
115    fn drop(&mut self) {
116        self.value = DynamicReturnableValue::Null;
117        InternedStore::release_returnable(self.hash);
118    }
119}
120
121// ------------------------------------------------------------------------------- [ DynamicReturnableValue ]
122
123#[derive(Clone, Debug)]
124pub enum DynamicReturnableValue {
125    Null,
126    Bool(bool),
127    JsonPointer(Arc<Box<RawValue>>),
128    JsonStatic(&'static RawValue),
129}
130
131impl DynamicReturnableValue {
132    fn raw_string_value(&self) -> Option<&str> {
133        match self {
134            DynamicReturnableValue::JsonPointer(raw) => Some(raw.as_ref().get()),
135            DynamicReturnableValue::JsonStatic(raw) => Some(raw.get()),
136            _ => None,
137        }
138    }
139}
140
141impl PartialEq for DynamicReturnableValue {
142    fn eq(&self, other: &Self) -> bool {
143        match (self, other) {
144            (DynamicReturnableValue::Null, DynamicReturnableValue::Null) => true,
145            (DynamicReturnableValue::Bool(a), DynamicReturnableValue::Bool(b)) => *a == *b,
146            (left, right) => left.raw_string_value() == right.raw_string_value(),
147        }
148    }
149}