influxdb_stream/
value.rs

1//! Value types for InfluxDB Flux query results.
2
3use chrono::{DateTime, FixedOffset};
4use ordered_float::OrderedFloat;
5
6/// Represents a value in an InfluxDB Flux query result.
7///
8/// This enum covers all data types that can appear in InfluxDB annotated CSV responses.
9#[derive(Clone, Debug, PartialEq)]
10pub enum Value {
11    /// String value.
12    String(String),
13
14    /// 64-bit floating point value.
15    Double(OrderedFloat<f64>),
16
17    /// Boolean value.
18    Bool(bool),
19
20    /// Signed 64-bit integer.
21    Long(i64),
22
23    /// Unsigned 64-bit integer.
24    UnsignedLong(u64),
25
26    /// Duration value (in nanoseconds, stored as chrono::Duration).
27    Duration(chrono::Duration),
28
29    /// Base64-encoded binary data.
30    Base64Binary(Vec<u8>),
31
32    /// RFC3339 timestamp with timezone.
33    TimeRFC(DateTime<FixedOffset>),
34
35    /// Null value.
36    Null,
37}
38
39impl Value {
40    /// Returns the value as a string reference if it is a `String` variant.
41    pub fn as_string(&self) -> Option<&str> {
42        match self {
43            Value::String(s) => Some(s),
44            _ => None,
45        }
46    }
47
48    /// Returns the value as an owned string if it is a `String` variant.
49    pub fn string(&self) -> Option<String> {
50        match self {
51            Value::String(s) => Some(s.clone()),
52            _ => None,
53        }
54    }
55
56    /// Returns the value as a f64 if it is a `Double` variant.
57    pub fn as_double(&self) -> Option<f64> {
58        match self {
59            Value::Double(f) => Some(f.into_inner()),
60            _ => None,
61        }
62    }
63
64    /// Returns the value as a bool if it is a `Bool` variant.
65    pub fn as_bool(&self) -> Option<bool> {
66        match self {
67            Value::Bool(b) => Some(*b),
68            _ => None,
69        }
70    }
71
72    /// Returns the value as an i64 if it is a `Long` variant.
73    pub fn as_long(&self) -> Option<i64> {
74        match self {
75            Value::Long(i) => Some(*i),
76            _ => None,
77        }
78    }
79
80    /// Returns the value as a u64 if it is an `UnsignedLong` variant.
81    pub fn as_unsigned_long(&self) -> Option<u64> {
82        match self {
83            Value::UnsignedLong(u) => Some(*u),
84            _ => None,
85        }
86    }
87
88    /// Returns the value as a chrono::Duration if it is a `Duration` variant.
89    pub fn as_duration(&self) -> Option<&chrono::Duration> {
90        match self {
91            Value::Duration(d) => Some(d),
92            _ => None,
93        }
94    }
95
96    /// Returns the value as a byte slice if it is a `Base64Binary` variant.
97    pub fn as_binary(&self) -> Option<&[u8]> {
98        match self {
99            Value::Base64Binary(b) => Some(b),
100            _ => None,
101        }
102    }
103
104    /// Returns the value as a DateTime if it is a `TimeRFC` variant.
105    pub fn as_time(&self) -> Option<&DateTime<FixedOffset>> {
106        match self {
107            Value::TimeRFC(t) => Some(t),
108            _ => None,
109        }
110    }
111
112    /// Returns true if this value is null.
113    pub fn is_null(&self) -> bool {
114        matches!(self, Value::Null)
115    }
116}
117
118impl std::fmt::Display for Value {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        match self {
121            Value::String(s) => write!(f, "{}", s),
122            Value::Double(d) => write!(f, "{}", d),
123            Value::Bool(b) => write!(f, "{}", b),
124            Value::Long(i) => write!(f, "{}", i),
125            Value::UnsignedLong(u) => write!(f, "{}", u),
126            Value::Duration(d) => write!(f, "{}ns", d.num_nanoseconds().unwrap_or(0)),
127            Value::Base64Binary(b) => write!(f, "<binary {} bytes>", b.len()),
128            Value::TimeRFC(t) => write!(f, "{}", t.to_rfc3339()),
129            Value::Null => write!(f, "null"),
130        }
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    // =========================================================================
139    // Value accessor tests
140    // =========================================================================
141
142    #[test]
143    fn test_as_string() {
144        let v = Value::String("hello".to_string());
145        assert_eq!(v.as_string(), Some("hello"));
146
147        // Wrong type returns None
148        assert_eq!(Value::Long(42).as_string(), None);
149        assert_eq!(Value::Null.as_string(), None);
150    }
151
152    #[test]
153    fn test_string() {
154        let v = Value::String("hello".to_string());
155        assert_eq!(v.string(), Some("hello".to_string()));
156
157        // Wrong type returns None
158        assert_eq!(Value::Long(42).string(), None);
159        assert_eq!(Value::Null.string(), None);
160    }
161
162    #[test]
163    fn test_as_double() {
164        let v = Value::Double(OrderedFloat::from(2.72));
165        assert_eq!(v.as_double(), Some(2.72));
166
167        // Wrong type returns None
168        assert_eq!(Value::Long(42).as_double(), None);
169        assert_eq!(Value::String("2.72".to_string()).as_double(), None);
170        assert_eq!(Value::Null.as_double(), None);
171    }
172
173    #[test]
174    fn test_as_bool() {
175        assert_eq!(Value::Bool(true).as_bool(), Some(true));
176        assert_eq!(Value::Bool(false).as_bool(), Some(false));
177
178        // Wrong type returns None
179        assert_eq!(Value::Long(1).as_bool(), None);
180        assert_eq!(Value::String("true".to_string()).as_bool(), None);
181        assert_eq!(Value::Null.as_bool(), None);
182    }
183
184    #[test]
185    fn test_as_long() {
186        assert_eq!(Value::Long(42).as_long(), Some(42));
187        assert_eq!(Value::Long(-100).as_long(), Some(-100));
188        assert_eq!(Value::Long(i64::MAX).as_long(), Some(i64::MAX));
189
190        // Wrong type returns None
191        assert_eq!(Value::UnsignedLong(42).as_long(), None);
192        assert_eq!(Value::Double(OrderedFloat::from(42.0)).as_long(), None);
193        assert_eq!(Value::Null.as_long(), None);
194    }
195
196    #[test]
197    fn test_as_unsigned_long() {
198        assert_eq!(Value::UnsignedLong(42).as_unsigned_long(), Some(42));
199        assert_eq!(
200            Value::UnsignedLong(u64::MAX).as_unsigned_long(),
201            Some(u64::MAX)
202        );
203
204        // Wrong type returns None
205        assert_eq!(Value::Long(42).as_unsigned_long(), None);
206        assert_eq!(
207            Value::Double(OrderedFloat::from(42.0)).as_unsigned_long(),
208            None
209        );
210        assert_eq!(Value::Null.as_unsigned_long(), None);
211    }
212
213    #[test]
214    fn test_as_duration() {
215        let dur = chrono::Duration::nanoseconds(1_000_000_000);
216        let v = Value::Duration(dur);
217        assert!(v.as_duration().is_some());
218        assert_eq!(v.as_duration().unwrap().num_seconds(), 1);
219
220        // Wrong type returns None
221        assert!(Value::Long(1000).as_duration().is_none());
222        assert!(Value::Null.as_duration().is_none());
223    }
224
225    #[test]
226    fn test_as_binary() {
227        let v = Value::Base64Binary(vec![1, 2, 3, 4]);
228        assert_eq!(v.as_binary(), Some(&[1u8, 2, 3, 4][..]));
229
230        // Wrong type returns None
231        assert!(Value::String("data".to_string()).as_binary().is_none());
232        assert!(Value::Null.as_binary().is_none());
233    }
234
235    #[test]
236    fn test_as_time() {
237        let dt = DateTime::parse_from_rfc3339("2023-11-14T12:00:00Z").unwrap();
238        let v = Value::TimeRFC(dt);
239        assert!(v.as_time().is_some());
240
241        // Wrong type returns None
242        assert!(Value::String("2023-11-14".to_string()).as_time().is_none());
243        assert!(Value::Long(1699963200).as_time().is_none());
244        assert!(Value::Null.as_time().is_none());
245    }
246
247    #[test]
248    fn test_is_null() {
249        assert!(Value::Null.is_null());
250
251        // Non-null values
252        assert!(!Value::String("".to_string()).is_null());
253        assert!(!Value::Long(0).is_null());
254        assert!(!Value::Bool(false).is_null());
255        assert!(!Value::Double(OrderedFloat::from(0.0)).is_null());
256    }
257
258    // =========================================================================
259    // Value Display tests
260    // =========================================================================
261
262    #[test]
263    fn test_display_string() {
264        let v = Value::String("hello world".to_string());
265        assert_eq!(v.to_string(), "hello world");
266    }
267
268    #[test]
269    fn test_display_double() {
270        let v = Value::Double(OrderedFloat::from(1.23456));
271        assert!(v.to_string().starts_with("1.23"));
272    }
273
274    #[test]
275    fn test_display_bool() {
276        assert_eq!(Value::Bool(true).to_string(), "true");
277        assert_eq!(Value::Bool(false).to_string(), "false");
278    }
279
280    #[test]
281    fn test_display_long() {
282        assert_eq!(Value::Long(42).to_string(), "42");
283        assert_eq!(Value::Long(-100).to_string(), "-100");
284    }
285
286    #[test]
287    fn test_display_unsigned_long() {
288        assert_eq!(Value::UnsignedLong(42).to_string(), "42");
289        assert_eq!(
290            Value::UnsignedLong(u64::MAX).to_string(),
291            "18446744073709551615"
292        );
293    }
294
295    #[test]
296    fn test_display_duration() {
297        let dur = chrono::Duration::nanoseconds(1_500_000_000);
298        let v = Value::Duration(dur);
299        assert_eq!(v.to_string(), "1500000000ns");
300    }
301
302    #[test]
303    fn test_display_base64_binary() {
304        let v = Value::Base64Binary(vec![1, 2, 3, 4, 5]);
305        assert_eq!(v.to_string(), "<binary 5 bytes>");
306    }
307
308    #[test]
309    fn test_display_time_rfc() {
310        let dt = DateTime::parse_from_rfc3339("2023-11-14T12:30:45Z").unwrap();
311        let v = Value::TimeRFC(dt);
312        assert!(v.to_string().contains("2023-11-14"));
313        assert!(v.to_string().contains("12:30:45"));
314    }
315
316    #[test]
317    fn test_display_null() {
318        assert_eq!(Value::Null.to_string(), "null");
319    }
320
321    // =========================================================================
322    // Value equality tests
323    // =========================================================================
324
325    #[test]
326    fn test_value_equality() {
327        assert_eq!(
328            Value::String("a".to_string()),
329            Value::String("a".to_string())
330        );
331        assert_ne!(
332            Value::String("a".to_string()),
333            Value::String("b".to_string())
334        );
335
336        assert_eq!(Value::Long(42), Value::Long(42));
337        assert_ne!(Value::Long(42), Value::Long(43));
338
339        assert_eq!(Value::Null, Value::Null);
340
341        // Different types are not equal
342        assert_ne!(Value::Long(42), Value::UnsignedLong(42));
343        assert_ne!(Value::String("42".to_string()), Value::Long(42));
344    }
345
346    #[test]
347    fn test_value_clone() {
348        let original = Value::String("test".to_string());
349        let cloned = original.clone();
350        assert_eq!(original, cloned);
351
352        let original = Value::Base64Binary(vec![1, 2, 3]);
353        let cloned = original.clone();
354        assert_eq!(original, cloned);
355    }
356}