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_serde_map(&self) -> Option<serde_json::Map<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_json(&self) -> Option<HashMap<String, JsonValue>> {
86        let bytes = match &self.value {
87            DynamicReturnableValue::JsonPointer(bytes) => bytes.get().as_bytes(),
88            DynamicReturnableValue::JsonStatic(bytes) => bytes.get().as_bytes(),
89            _ => return None,
90        };
91
92        match serde_json::from_slice(bytes) {
93            Ok(json) => Some(json),
94            Err(e) => {
95                log_e!(TAG, "Failed to parse json. Error: {}", e);
96                None
97            }
98        }
99    }
100
101    pub fn get_hash(&self) -> u64 {
102        self.hash
103    }
104}
105
106impl<'de> Deserialize<'de> for DynamicReturnable {
107    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
108    where
109        D: Deserializer<'de>,
110    {
111        let raw_value_ref: Cow<RawValue> = Deserialize::deserialize(deserializer)?;
112        Ok(InternedStore::get_or_intern_returnable(raw_value_ref))
113    }
114}
115
116impl Serialize for DynamicReturnable {
117    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
118    where
119        S: Serializer,
120    {
121        match &self.value {
122            DynamicReturnableValue::JsonPointer(raw) => raw.serialize(serializer),
123            DynamicReturnableValue::JsonStatic(raw) => raw.serialize(serializer),
124            DynamicReturnableValue::Null => serializer.serialize_none(),
125            DynamicReturnableValue::Bool(value) => serializer.serialize_bool(*value),
126        }
127    }
128}
129
130impl Drop for DynamicReturnable {
131    fn drop(&mut self) {
132        self.value = DynamicReturnableValue::Null;
133        InternedStore::release_returnable(self.hash);
134    }
135}
136
137// ------------------------------------------------------------------------------- [ DynamicReturnableValue ]
138
139#[derive(Clone, Debug)]
140pub enum DynamicReturnableValue {
141    Null,
142    Bool(bool),
143    JsonPointer(Arc<Box<RawValue>>),
144    JsonStatic(&'static RawValue),
145}
146
147impl DynamicReturnableValue {
148    fn raw_string_value(&self) -> Option<&str> {
149        match self {
150            DynamicReturnableValue::JsonPointer(raw) => Some(raw.as_ref().get()),
151            DynamicReturnableValue::JsonStatic(raw) => Some(raw.get()),
152            _ => None,
153        }
154    }
155}
156
157impl PartialEq for DynamicReturnableValue {
158    fn eq(&self, other: &Self) -> bool {
159        match (self, other) {
160            (DynamicReturnableValue::Null, DynamicReturnableValue::Null) => true,
161            (DynamicReturnableValue::Bool(a), DynamicReturnableValue::Bool(b)) => *a == *b,
162            (left, right) => left.raw_string_value() == right.raw_string_value(),
163        }
164    }
165}