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::hashing::ahash_str;
7
8use super::dynamic_string::DynamicString;
9
10#[macro_export]
11macro_rules! dyn_value {
12 ($x:expr) => {{
13 $crate::DynamicValue::from_json_value($x)
14 }};
15}
16
17#[derive(Debug, Clone, Default)]
18pub struct DynamicValue {
19 pub null: Option<()>,
20 pub bool_value: Option<bool>,
21 pub int_value: Option<i64>,
22 pub float_value: Option<f64>,
23 pub timestamp_value: Option<i64>,
24 pub string_value: Option<DynamicString>,
25 pub array_value: Option<Vec<DynamicValue>>,
26 pub object_value: Option<HashMap<String, DynamicValue>>,
27 pub json_value: JsonValue,
28 pub hash_value: u64,
29}
30
31impl DynamicValue {
32 #[must_use]
33 pub fn new() -> Self {
34 Self::default()
35 }
36
37 #[must_use]
38 pub fn from_json_value(value: impl Serialize) -> Self {
39 Self::from(json!(value))
40 }
41
42 #[must_use]
43 pub fn for_timestamp_evaluation(timestamp: i64) -> DynamicValue {
44 DynamicValue {
45 int_value: Some(timestamp),
46 ..DynamicValue::default()
47 }
48 }
49
50 fn try_parse_timestamp(s: &str) -> Option<i64> {
51 if let Ok(ts) = s.parse::<i64>() {
52 return Some(ts);
53 }
54
55 if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
56 return Some(dt.timestamp_millis());
57 }
58
59 if let Ok(ndt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
60 return Some(ndt.and_utc().timestamp_millis());
61 }
62
63 None
64 }
65}
66
67impl PartialEq for DynamicValue {
68 fn eq(&self, other: &Self) -> bool {
69 self.null == other.null
70 && self.bool_value == other.bool_value
71 && self.int_value == other.int_value
72 && self.float_value == other.float_value
73 && self.string_value == other.string_value
74 && self.array_value == other.array_value
75 && self.object_value == other.object_value
76 }
77}
78
79impl Serialize for DynamicValue {
82 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
83 where
84 S: Serializer,
85 {
86 self.json_value.serialize(serializer)
87 }
88}
89
90impl<'de> Deserialize<'de> for DynamicValue {
91 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
92 where
93 D: Deserializer<'de>,
94 {
95 let json_value = JsonValue::deserialize(deserializer)?;
96 Ok(DynamicValue::from(json_value))
97 }
98}
99
100impl From<JsonValue> for DynamicValue {
103 fn from(json_value: JsonValue) -> Self {
104 let hash_value = ahash_str(&json_value.to_string());
105
106 match &json_value {
107 JsonValue::Null => DynamicValue {
108 null: Some(()),
109 json_value,
110 hash_value,
111 ..DynamicValue::new()
112 },
113
114 JsonValue::Bool(b) => DynamicValue {
115 bool_value: Some(*b),
116 string_value: Some(DynamicString::from(b.to_string())),
117 json_value,
118 hash_value,
119 ..DynamicValue::new()
120 },
121
122 JsonValue::Number(n) => {
123 let float_value = n.as_f64();
124 let int_value = n.as_i64();
125
126 let string_value = float_value
127 .map(|f| f.to_string())
128 .or_else(|| int_value.map(|i| i.to_string()))
129 .or_else(|| Some(json_value.to_string()));
130
131 DynamicValue {
132 float_value,
133 int_value,
134 string_value: string_value.map(DynamicString::from),
135 json_value,
136 hash_value,
137 ..DynamicValue::new()
138 }
139 }
140
141 JsonValue::String(s) => {
142 let timestamp_value = Self::try_parse_timestamp(s);
143 let float_value = s.parse().ok();
144 let int_value = s.parse().ok();
145 DynamicValue {
146 string_value: Some(DynamicString::from(s.clone())),
147 json_value,
148 timestamp_value,
149 int_value,
150 float_value,
151 hash_value,
152 ..DynamicValue::new()
153 }
154 }
155
156 JsonValue::Array(arr) => DynamicValue {
157 array_value: Some(arr.iter().map(|v| DynamicValue::from(v.clone())).collect()),
158 string_value: Some(DynamicString::from(json_value.to_string())),
159 json_value,
160 hash_value,
161 ..DynamicValue::new()
162 },
163
164 JsonValue::Object(obj) => DynamicValue {
165 object_value: Some(
166 obj.into_iter()
167 .map(|(k, v)| (k.clone(), DynamicValue::from(v.clone())))
168 .collect(),
169 ),
170 json_value,
171 hash_value,
172 ..DynamicValue::new()
173 },
174 }
175 }
176}
177
178impl From<String> for DynamicValue {
179 fn from(value: String) -> Self {
180 Self::from(json!(value))
181 }
182}
183
184impl From<&str> for DynamicValue {
185 fn from(value: &str) -> Self {
186 Self::from(json!(value))
187 }
188}
189
190impl From<usize> for DynamicValue {
191 fn from(value: usize) -> Self {
192 Self::from(json!(value))
193 }
194}
195
196impl From<i64> for DynamicValue {
197 fn from(value: i64) -> Self {
198 Self::from(json!(value))
199 }
200}
201
202impl From<i32> for DynamicValue {
203 fn from(value: i32) -> Self {
204 Self::from(json!(value))
205 }
206}
207
208impl From<f64> for DynamicValue {
209 fn from(value: f64) -> Self {
210 Self::from(json!(value))
211 }
212}
213
214impl From<bool> for DynamicValue {
215 fn from(value: bool) -> Self {
216 Self::from(json!(value))
217 }
218}
219
220impl From<Vec<JsonValue>> for DynamicValue {
221 fn from(value: Vec<JsonValue>) -> Self {
222 DynamicValue::from(json!(value))
223 }
224}