statsig_rust/evaluation/
dynamic_value.rs1use chrono::{DateTime, NaiveDateTime};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use serde_json::{json, Value as JsonValue};
4use std::{collections::HashMap, fmt::Debug};
5
6use crate::{
7 hashing::{self, ahash_str},
8 log_w,
9};
10
11use super::dynamic_string::DynamicString;
12
13const TAG: &str = "DynamicValue";
14
15#[macro_export]
16macro_rules! dyn_value {
17 ($x:expr) => {{
18 $crate::DynamicValue::from_json_value($x)
19 }};
20}
21
22#[derive(Debug, Clone, Default)]
23pub struct DynamicValue {
24 pub null: Option<()>,
25 pub bool_value: Option<bool>,
26 pub int_value: Option<i64>,
27 pub float_value: Option<f64>,
28 pub timestamp_value: Option<i64>,
29 pub string_value: Option<DynamicString>,
30 pub array_value: Option<Vec<DynamicValue>>,
31 pub object_value: Option<HashMap<String, DynamicValue>>,
32 pub json_value: JsonValue,
33 pub hash_value: u64,
34}
35
36impl DynamicValue {
37 #[must_use]
38 pub fn new() -> Self {
39 Self::default()
40 }
41
42 #[must_use]
43 pub fn from_json_value(value: impl Serialize) -> Self {
44 Self::from(json!(value))
45 }
46
47 #[must_use]
48 pub fn for_timestamp_evaluation(timestamp: i64) -> DynamicValue {
49 DynamicValue {
50 int_value: Some(timestamp),
51 ..DynamicValue::default()
52 }
53 }
54
55 fn try_parse_timestamp(s: &str) -> Option<i64> {
56 if let Ok(ts) = s.parse::<i64>() {
58 return Some(ts);
59 }
60
61 if s.len() < 8 || s.len() > 20 || (!s.contains('-') && !s.contains('T') && !s.contains(':'))
63 {
64 return None;
65 }
66
67 if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
69 return Some(dt.timestamp_millis());
70 }
71
72 if let Ok(ndt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
74 return Some(ndt.and_utc().timestamp_millis());
75 }
76
77 None
78 }
79}
80
81impl PartialEq for DynamicValue {
82 fn eq(&self, other: &Self) -> bool {
83 self.null == other.null
84 && self.bool_value == other.bool_value
85 && self.int_value == other.int_value
86 && self.float_value == other.float_value
87 && self.string_value == other.string_value
88 && self.array_value == other.array_value
89 && self.object_value == other.object_value
90 }
91}
92
93impl Serialize for DynamicValue {
96 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
97 where
98 S: Serializer,
99 {
100 self.json_value.serialize(serializer)
101 }
102}
103
104impl<'de> Deserialize<'de> for DynamicValue {
105 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
106 where
107 D: Deserializer<'de>,
108 {
109 let json_value = JsonValue::deserialize(deserializer)?;
110 Ok(DynamicValue::from(json_value))
111 }
112}
113
114impl From<JsonValue> for DynamicValue {
117 fn from(json_value: JsonValue) -> Self {
118 let mut stringified_json_value = None;
120 let hash_value = if let JsonValue::String(s) = &json_value {
121 ahash_str(s)
122 } else {
123 let actual = json_value.to_string();
124 let hash = ahash_str(&actual);
125 stringified_json_value = Some(actual);
126 hash
127 };
128
129 match &json_value {
130 JsonValue::Null => DynamicValue {
131 null: Some(()),
132 json_value,
133 hash_value,
134 ..DynamicValue::new()
135 },
136
137 JsonValue::Bool(b) => DynamicValue {
138 bool_value: Some(*b),
139 string_value: Some(DynamicString::from(b.to_string())),
140 json_value,
141 hash_value,
142 ..DynamicValue::new()
143 },
144
145 JsonValue::Number(n) => {
146 let mut float_value = n.as_f64();
147 let mut int_value = n.as_i64();
148 if let (Some(f), None) = (float_value, int_value) {
149 let iv = f as i64;
150 if iv as f64 == f {
151 int_value = Some(iv);
152 }
153 } else if let (None, Some(i)) = (float_value, int_value) {
154 let fv = i as f64;
155 if fv as i64 == i {
156 float_value = Some(fv)
157 }
158 }
159
160 let string_value = float_value
161 .map(|f| f.to_string())
162 .or_else(|| int_value.map(|i| i.to_string()))
163 .or(stringified_json_value);
164
165 DynamicValue {
166 float_value,
167 int_value,
168 string_value: string_value.map(DynamicString::from),
169 json_value,
170 hash_value,
171 ..DynamicValue::new()
172 }
173 }
174
175 JsonValue::String(s) => {
176 let timestamp_value = Self::try_parse_timestamp(s);
177 let float_value = s.parse().ok();
178 let int_value = s.parse().ok();
179 DynamicValue {
180 string_value: Some(DynamicString::from(s.clone())),
181 json_value,
182 timestamp_value,
183 int_value,
184 float_value,
185 hash_value,
186 ..DynamicValue::new()
187 }
188 }
189
190 JsonValue::Array(arr) => DynamicValue {
191 array_value: Some(arr.iter().map(|v| DynamicValue::from(v.clone())).collect()),
192 string_value: Some(DynamicString::from(
193 stringified_json_value.unwrap_or(json_value.to_string()),
194 )),
195 json_value,
196 hash_value,
197 ..DynamicValue::new()
198 },
199
200 JsonValue::Object(obj) => DynamicValue {
201 object_value: Some(
202 obj.into_iter()
203 .map(|(k, v)| (k.clone(), DynamicValue::from(v.clone())))
204 .collect(),
205 ),
206 json_value,
207 hash_value,
208 ..DynamicValue::new()
209 },
210 }
211 }
212}
213
214impl From<String> for DynamicValue {
215 fn from(value: String) -> Self {
216 Self::from(serde_json::Value::String(value))
217 }
218}
219
220impl From<&str> for DynamicValue {
221 fn from(value: &str) -> Self {
222 Self::from(serde_json::Value::String(value.to_string()))
223 }
224}
225
226impl From<usize> for DynamicValue {
227 fn from(value: usize) -> Self {
228 Self::from(serde_json::Value::Number(serde_json::Number::from(value)))
229 }
230}
231
232impl From<i64> for DynamicValue {
233 fn from(value: i64) -> Self {
234 Self::from(serde_json::Value::Number(serde_json::Number::from(value)))
235 }
236}
237
238impl From<i32> for DynamicValue {
239 fn from(value: i32) -> Self {
240 Self::from(serde_json::Value::Number(serde_json::Number::from(value)))
241 }
242}
243
244impl From<f64> for DynamicValue {
245 fn from(value: f64) -> Self {
246 let num = match serde_json::Number::from_f64(value) {
247 Some(num) => num,
248 None => {
249 log_w!(
250 TAG,
251 "Failed to convert f64 to serde_json::Number: {}",
252 value
253 );
254 serde_json::Number::from(value as i64)
255 }
256 };
257
258 Self::from(serde_json::Value::Number(num))
259 }
260}
261
262impl From<bool> for DynamicValue {
263 fn from(value: bool) -> Self {
264 Self::from(serde_json::Value::Bool(value))
265 }
266}
267
268impl From<Vec<JsonValue>> for DynamicValue {
269 fn from(value: Vec<JsonValue>) -> Self {
270 DynamicValue::from(serde_json::Value::Array(value))
271 }
272}
273
274impl From<Vec<DynamicValue>> for DynamicValue {
275 fn from(value: Vec<DynamicValue>) -> Self {
276 let json_value = json!(value);
277 let string_value = DynamicString::from(json_value.to_string());
278 let hash_value = hashing::hash_one(value.iter().map(|v| v.hash_value).collect::<Vec<_>>());
279
280 DynamicValue {
281 hash_value,
282 array_value: Some(value),
283 json_value,
284 string_value: Some(string_value),
285 ..DynamicValue::default()
286 }
287 }
288}
289
290impl From<HashMap<String, DynamicValue>> for DynamicValue {
291 fn from(value: HashMap<String, DynamicValue>) -> Self {
292 let json_value = json!(value);
293 let hash_value =
294 hashing::hash_one(value.values().map(|v| v.hash_value).collect::<Vec<_>>());
295
296 DynamicValue {
297 hash_value,
298 object_value: Some(value),
299 json_value,
300 ..DynamicValue::default()
301 }
302 }
303}