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