statsig_rust/evaluation/
dynamic_returnable.rs1use crate::{hashing::djb2, log_e};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use serde_json::{
4 value::{to_raw_value, RawValue},
5 Value as JsonValue,
6};
7use std::{
8 collections::HashMap,
9 sync::{Arc, Mutex, Weak},
10};
11
12const TAG: &str = "DynamicReturnable";
13
14lazy_static::lazy_static! {
15 pub(crate) static ref MEMOIZED_VALUES: Mutex<HashMap<String, Weak<MemoizedValue>>> =
16 Mutex::new(HashMap::new());
17
18 static ref EMPTY_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
19 hash: "".to_string(),
20 value: Arc::new(MemoizedValue {
21 raw_value: None,
22 bool_value: None,
23 json_value: None,
24 }),
25 };
26}
27
28#[derive(Clone, PartialEq, Debug)]
29pub struct DynamicReturnable {
30 hash: String,
31 value: Arc<MemoizedValue>,
32}
33
34impl DynamicReturnable {
35 pub fn empty() -> Self {
36 EMPTY_DYNAMIC_RETURNABLE.clone()
37 }
38
39 pub fn from_map(value: HashMap<String, JsonValue>) -> Self {
40 let raw_value = match to_raw_value(&value) {
41 Ok(raw_value) => raw_value,
42 Err(e) => {
43 log_e!(TAG, "Failed to convert map to raw value: {}", e);
44 return Self::empty();
45 }
46 };
47
48 let hash = djb2(raw_value.get());
49 let value = Arc::new(MemoizedValue {
50 raw_value: Some(raw_value),
51 bool_value: None,
52 json_value: Some(value.clone()),
53 });
54
55 Self::new(hash.to_string(), value)
56 }
57
58 pub fn get_bool(&self) -> Option<bool> {
59 self.value.bool_value
60 }
61
62 pub fn get_json(&self) -> Option<HashMap<String, JsonValue>> {
63 self.value.json_value.clone()
64 }
65
66 pub fn get_json_ref(&self) -> Option<&HashMap<String, JsonValue>> {
67 self.value.json_value.as_ref()
68 }
69
70 fn new(hash: String, value: Arc<MemoizedValue>) -> Self {
71 let weak_value = Arc::downgrade(&value);
72 set_memoized_value(&hash, weak_value);
73
74 Self { hash, value }
75 }
76}
77
78impl<'de> Deserialize<'de> for DynamicReturnable {
79 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
80 where
81 D: Deserializer<'de>,
82 {
83 let raw_value_ref: &'de RawValue = Deserialize::deserialize(deserializer)?;
84
85 let raw_value_str = raw_value_ref.get();
86 let hash = djb2(raw_value_str);
87
88 if let Some(value) = get_memoized_value(&hash) {
89 return Ok(DynamicReturnable { hash, value });
90 }
91
92 let raw_value = raw_value_ref.to_owned();
93 let value = MemoizedValue::new(raw_value);
94
95 let new_returnable = DynamicReturnable::new(hash.clone(), value);
96
97 Ok(new_returnable)
98 }
99}
100
101impl Serialize for DynamicReturnable {
102 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
103 where
104 S: Serializer,
105 {
106 if let Some(bool_value) = self.value.bool_value {
107 return bool_value.serialize(serializer);
108 }
109
110 if let Some(raw_value) = &self.value.raw_value {
111 return raw_value.serialize(serializer);
112 }
113
114 if let Some(json_value) = &self.value.json_value {
115 return json_value.serialize(serializer);
116 }
117
118 JsonValue::Null.serialize(serializer)
119 }
120}
121
122impl Drop for DynamicReturnable {
123 fn drop(&mut self) {
124 let mut memo = match MEMOIZED_VALUES.lock() {
125 Ok(values) => values,
126 Err(e) => {
127 log_e!(TAG, "Failed to lock memoized values: {}", e);
128 return;
129 }
130 };
131
132 let found = match memo.get(&self.hash) {
133 Some(value) => value,
134 None => return,
135 };
136
137 if found.strong_count() == 1 {
138 memo.remove(&self.hash);
139 }
140 }
141}
142
143#[derive(Debug, Clone)]
144pub(crate) struct MemoizedValue {
145 pub(crate) raw_value: Option<Box<RawValue>>,
146 pub(crate) bool_value: Option<bool>,
147 pub(crate) json_value: Option<HashMap<String, JsonValue>>,
148}
149
150impl PartialEq for MemoizedValue {
151 fn eq(&self, other: &Self) -> bool {
152 self.raw_value.as_ref().map(|v| v.get()) == other.raw_value.as_ref().map(|v| v.get())
153 && self.bool_value == other.bool_value
154 && self.json_value == other.json_value
155 }
156}
157
158impl MemoizedValue {
159 fn new(raw_value: Box<RawValue>) -> Arc<Self> {
160 let value = match raw_value.get() {
161 "true" => Self::from_bool(true),
162 "false" => Self::from_bool(false),
163 _ => Self::from_raw_value(raw_value),
164 };
165
166 Arc::new(value)
167 }
168
169 fn from_bool(bool_value: bool) -> Self {
170 Self {
171 raw_value: None,
172 bool_value: Some(bool_value),
173 json_value: None,
174 }
175 }
176
177 fn from_raw_value(raw_value: Box<RawValue>) -> Self {
178 let json_value = match serde_json::from_str(raw_value.get()) {
179 Ok(json_value) => json_value,
180 Err(e) => {
181 log_e!(TAG, "Failed to parse json: {}", e);
182 None
183 }
184 };
185
186 Self {
187 raw_value: Some(raw_value),
188 bool_value: None,
189 json_value,
190 }
191 }
192}
193
194fn get_memoized_value(hash: &str) -> Option<Arc<MemoizedValue>> {
195 let mut memoized_values = match MEMOIZED_VALUES.lock() {
196 Ok(values) => values,
197 Err(e) => {
198 log_e!(TAG, "Failed to lock memoized values: {}", e);
199 return None;
200 }
201 };
202
203 let found = memoized_values.get(hash)?;
204
205 match found.upgrade() {
206 Some(value) => Some(value),
207 None => {
208 memoized_values.remove(hash);
209 None
210 }
211 }
212}
213
214fn set_memoized_value(hash: &str, value: Weak<MemoizedValue>) {
215 match MEMOIZED_VALUES.lock() {
216 Ok(mut values) => {
217 values.insert(hash.to_string(), value);
218 }
219 Err(e) => {
220 log_e!(TAG, "Failed to lock memoized values: {}", e);
221 }
222 };
223}