statsig_rust/evaluation/
dynamic_value.rs

1use 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
79// ------------------------------------------------------------------------------- [Serialization]
80
81impl 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
100// ------------------------------------------------------------------------------- [From<T> Implementations]
101
102impl 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}