Skip to main content

wide_log/
value.rs

1use faststr::FastStr;
2use serde::ser::{Serialize, Serializer};
3use smallvec::SmallVec;
4
5use crate::key::Key;
6use crate::wide_event::WideEvent;
7
8/// A JSON value stored in a wide event.
9///
10/// All variants are safe to construct, clone, and drop — the compiler
11/// handles destructors automatically. No `unsafe` is required anywhere
12/// in this type.
13pub enum Value<K: Key> {
14    Null,
15    Bool(bool),
16    I64(i64),
17    U64(u64),
18    F64(f64),
19    Str(FastStr),
20    StaticStr(&'static str),
21    Array(Box<SmallVec<[Value<K>; 8]>>),
22    Object(Box<WideEvent<K>>),
23}
24
25impl<K: Key> Value<K> {
26    /// Creates an Object value from a WideEvent.
27    #[inline]
28    pub(crate) fn from_object(ev: WideEvent<K>) -> Self {
29        Value::Object(Box::new(ev))
30    }
31
32    /// Returns `true` if this value is an `Object`.
33    #[inline]
34    pub(crate) fn is_object(&self) -> bool {
35        matches!(self, Value::Object(_))
36    }
37}
38
39#[cfg(test)]
40impl<K: Key> Value<K> {
41    /// Creates an Array value from a SmallVec of Values.
42    #[inline]
43    pub(crate) fn from_array(arr: SmallVec<[Value<K>; 8]>) -> Self {
44        Value::Array(Box::new(arr))
45    }
46
47    #[inline]
48    pub(crate) fn as_str(&self) -> Option<&str> {
49        match self {
50            Value::Str(s) => Some(s.as_str()),
51            Value::StaticStr(s) => Some(s),
52            _ => None,
53        }
54    }
55
56    /// Returns a reference to the Array if this value is an Array.
57    #[inline]
58    pub(crate) fn as_array_ref(&self) -> Option<&SmallVec<[Value<K>; 8]>> {
59        match self {
60            Value::Array(arr) => Some(arr),
61            _ => None,
62        }
63    }
64
65    /// Creates a `StaticStr` value from a `&'static str` — zero-copy, zero-allocation.
66    #[inline]
67    pub(crate) fn from_static_str(s: &'static str) -> Self {
68        Value::StaticStr(s)
69    }
70}
71
72impl<K: Key> Clone for Value<K> {
73    #[inline]
74    fn clone(&self) -> Self {
75        match self {
76            Value::Null => Value::Null,
77            Value::Bool(b) => Value::Bool(*b),
78            Value::I64(i) => Value::I64(*i),
79            Value::U64(u) => Value::U64(*u),
80            Value::F64(f) => Value::F64(*f),
81            Value::Str(s) => Value::Str(s.clone()),
82            Value::StaticStr(s) => Value::StaticStr(s),
83            Value::Array(arr) => Value::Array(arr.clone()),
84            Value::Object(obj) => Value::Object(obj.clone()),
85        }
86    }
87}
88
89impl<K: Key> std::fmt::Debug for Value<K> {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        match self {
92            Value::Null => f.write_str("Value::Null"),
93            Value::Bool(b) => f.debug_tuple("Value::Bool").field(b).finish(),
94            Value::I64(i) => f.debug_tuple("Value::I64").field(i).finish(),
95            Value::U64(u) => f.debug_tuple("Value::U64").field(u).finish(),
96            Value::F64(fl) => f.debug_tuple("Value::F64").field(fl).finish(),
97            Value::Str(s) => f.debug_tuple("Value::Str").field(s).finish(),
98            Value::StaticStr(s) => f.debug_tuple("Value::StaticStr").field(s).finish(),
99            Value::Array(arr) => f.debug_tuple("Value::Array").field(arr).finish(),
100            Value::Object(obj) => f.debug_tuple("Value::Object").field(obj).finish(),
101        }
102    }
103}
104
105impl<K: Key> Serialize for Value<K> {
106    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
107        match self {
108            Value::Null => serializer.serialize_unit(),
109            Value::Bool(b) => serializer.serialize_bool(*b),
110            Value::I64(i) => serializer.serialize_i64(*i),
111            Value::U64(u) => serializer.serialize_u64(*u),
112            Value::F64(f) => serializer.serialize_f64(*f),
113            Value::Str(s) => serializer.serialize_str(s.as_str()),
114            Value::StaticStr(s) => serializer.serialize_str(s),
115            Value::Array(arr) => arr.serialize(serializer),
116            Value::Object(obj) => obj.serialize(serializer),
117        }
118    }
119}
120
121// ── From impls ──
122
123impl<K: Key> From<bool> for Value<K> {
124    #[inline]
125    fn from(b: bool) -> Self {
126        Value::Bool(b)
127    }
128}
129
130impl<K: Key> From<i64> for Value<K> {
131    #[inline]
132    fn from(n: i64) -> Self {
133        Value::I64(n)
134    }
135}
136
137impl<K: Key> From<u64> for Value<K> {
138    #[inline]
139    fn from(n: u64) -> Self {
140        Value::U64(n)
141    }
142}
143
144impl<K: Key> From<f64> for Value<K> {
145    #[inline]
146    fn from(n: f64) -> Self {
147        Value::F64(n)
148    }
149}
150
151impl<K: Key> From<&str> for Value<K> {
152    #[inline]
153    fn from(s: &str) -> Self {
154        Value::Str(FastStr::new(s))
155    }
156}
157
158impl<K: Key> From<String> for Value<K> {
159    #[inline]
160    fn from(s: String) -> Self {
161        Value::Str(FastStr::from_string(s))
162    }
163}
164
165impl<K: Key> From<FastStr> for Value<K> {
166    #[inline]
167    fn from(s: FastStr) -> Self {
168        Value::Str(s)
169    }
170}
171
172impl<K: Key> From<()> for Value<K> {
173    #[inline]
174    fn from(_: ()) -> Self {
175        Value::Null
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182    use crate::key::test_support::TestKey;
183
184    #[test]
185    fn from_bool() {
186        assert!(matches!(Value::<TestKey>::from(true), Value::Bool(true)));
187        assert!(matches!(Value::<TestKey>::from(false), Value::Bool(false)));
188    }
189
190    #[test]
191    fn from_i64() {
192        let v = Value::<TestKey>::from(-42i64);
193        assert!(matches!(v, Value::I64(-42)));
194    }
195
196    #[test]
197    fn from_u64() {
198        let v = Value::<TestKey>::from(99u64);
199        assert!(matches!(v, Value::U64(99)));
200    }
201
202    #[test]
203    fn from_f64() {
204        let v = Value::<TestKey>::from(3.15f64);
205        assert!(matches!(v, Value::F64(_)));
206    }
207
208    #[test]
209    fn from_str_is_string() {
210        let v = Value::<TestKey>::from("hello");
211        assert!(matches!(v, Value::Str(_)));
212        assert_eq!(v.as_str(), Some("hello"));
213    }
214
215    #[test]
216    fn from_static_str() {
217        let v = Value::<TestKey>::from_static_str("world");
218        assert!(matches!(v, Value::StaticStr(_)));
219        assert_eq!(v.as_str(), Some("world"));
220    }
221
222    #[test]
223    fn from_owned_string_is_string() {
224        let v = Value::<TestKey>::from("world".to_string());
225        assert!(matches!(v, Value::Str(_)));
226        assert_eq!(v.as_str(), Some("world"));
227    }
228
229    #[test]
230    fn from_unit_is_null() {
231        assert!(matches!(Value::<TestKey>::from(()), Value::Null));
232    }
233
234    #[test]
235    fn serialize_null() {
236        let v = Value::<TestKey>::from(());
237        let s = sonic_rs::to_string(&v).unwrap();
238        assert_eq!(s, "null");
239    }
240
241    #[test]
242    fn serialize_scalars() {
243        assert_eq!(
244            sonic_rs::to_string(&Value::<TestKey>::from(true)).unwrap(),
245            "true"
246        );
247        assert_eq!(
248            sonic_rs::to_string(&Value::<TestKey>::from(false)).unwrap(),
249            "false"
250        );
251        assert_eq!(
252            sonic_rs::to_string(&Value::<TestKey>::from(-7i64)).unwrap(),
253            "-7"
254        );
255        assert_eq!(
256            sonic_rs::to_string(&Value::<TestKey>::from(42u64)).unwrap(),
257            "42"
258        );
259        assert_eq!(
260            sonic_rs::to_string(&Value::<TestKey>::from("hi")).unwrap(),
261            "\"hi\""
262        );
263    }
264
265    #[test]
266    fn serialize_static_str() {
267        let v = Value::<TestKey>::from_static_str("hello");
268        assert_eq!(sonic_rs::to_string(&v).unwrap(), "\"hello\"");
269    }
270
271    #[test]
272    fn serialize_array() {
273        let arr: SmallVec<[Value<TestKey>; 8]> =
274            smallvec::smallvec![Value::from(1i64), Value::from(2i64)];
275        let v = Value::<TestKey>::from_array(arr);
276        assert_eq!(sonic_rs::to_string(&v).unwrap(), "[1,2]");
277    }
278
279    #[test]
280    fn clone_works() {
281        let v = Value::<TestKey>::from("hello");
282        let v2 = v.clone();
283        assert_eq!(v2.as_str(), Some("hello"));
284    }
285
286    #[test]
287    fn clone_static_str() {
288        let v = Value::<TestKey>::from_static_str("world");
289        let v2 = v.clone();
290        assert_eq!(v2.as_str(), Some("world"));
291    }
292
293    #[test]
294    fn clone_object() {
295        let mut ev = WideEvent::<TestKey>::new();
296        ev.add(TestKey::Status, "ok");
297        let v = Value::<TestKey>::from_object(ev);
298        let v2 = v.clone();
299        assert!(matches!(v2, Value::Object(_)));
300        let json = sonic_rs::to_string(&v2).unwrap();
301        assert!(json.contains("\"status\":\"ok\""));
302    }
303}