Skip to main content

ardupilot_binlog/
value.rs

1use std::fmt;
2
3/// A decoded field value from a DataFlash log entry.
4///
5/// ```
6/// use ardupilot_binlog::FieldValue;
7///
8/// let v = FieldValue::Float(45.0);
9/// assert_eq!(v.as_f64(), Some(45.0));
10/// assert_eq!(v.to_string(), "45");
11/// ```
12#[derive(Debug, Clone, PartialEq)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub enum FieldValue {
15    /// Integer values (b, B, h, H, i, I, q, M, L)
16    Int(i64),
17    /// Unsigned 64-bit integer (Q) — separate because u64 can exceed i64 range
18    Uint(u64),
19    /// Floating-point values (f, d) and pre-scaled values (c, C, e, E already divided by 100)
20    Float(f64),
21    /// String values (n, N, Z) — null bytes trimmed
22    String(String),
23    /// Array of i16 values (a)
24    Array(Vec<i16>),
25}
26
27impl FieldValue {
28    /// Convert to f64 for numeric use. Returns None for String and Array variants.
29    pub fn as_f64(&self) -> Option<f64> {
30        match self {
31            FieldValue::Int(v) => Some(*v as f64),
32            FieldValue::Uint(v) => Some(*v as f64),
33            FieldValue::Float(v) => Some(*v),
34            FieldValue::String(_) | FieldValue::Array(_) => None,
35        }
36    }
37
38    /// Convert to i64. Returns None for non-integer variants.
39    pub fn as_i64(&self) -> Option<i64> {
40        match self {
41            FieldValue::Int(v) => Some(*v),
42            _ => None,
43        }
44    }
45
46    /// Convert to u64. Returns None for non-Uint variants.
47    pub fn as_u64(&self) -> Option<u64> {
48        match self {
49            FieldValue::Uint(v) => Some(*v),
50            _ => None,
51        }
52    }
53
54    /// Get string reference. Returns None for non-string variants.
55    pub fn as_str(&self) -> Option<&str> {
56        match self {
57            FieldValue::String(s) => Some(s),
58            _ => None,
59        }
60    }
61}
62
63impl fmt::Display for FieldValue {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        match self {
66            FieldValue::Int(v) => write!(f, "{v}"),
67            FieldValue::Uint(v) => write!(f, "{v}"),
68            FieldValue::Float(v) => write!(f, "{v}"),
69            FieldValue::String(s) => write!(f, "{s}"),
70            FieldValue::Array(arr) => {
71                write!(f, "[")?;
72                for (i, v) in arr.iter().enumerate() {
73                    if i > 0 {
74                        write!(f, ", ")?;
75                    }
76                    write!(f, "{v}")?;
77                }
78                write!(f, "]")
79            }
80        }
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn as_f64_int() {
90        assert_eq!(FieldValue::Int(42).as_f64(), Some(42.0));
91        assert_eq!(FieldValue::Int(-100).as_f64(), Some(-100.0));
92    }
93
94    #[test]
95    fn as_f64_uint() {
96        assert_eq!(FieldValue::Uint(u64::MAX).as_f64(), Some(u64::MAX as f64));
97    }
98
99    #[test]
100    fn as_f64_float() {
101        assert_eq!(FieldValue::Float(1.23).as_f64(), Some(1.23));
102    }
103
104    #[test]
105    fn as_f64_string_returns_none() {
106        assert_eq!(FieldValue::String("hello".into()).as_f64(), None);
107    }
108
109    #[test]
110    fn as_f64_array_returns_none() {
111        assert_eq!(FieldValue::Array(vec![1, 2, 3]).as_f64(), None);
112    }
113
114    #[test]
115    fn as_i64() {
116        assert_eq!(FieldValue::Int(-5).as_i64(), Some(-5));
117        assert_eq!(FieldValue::Uint(5).as_i64(), None);
118        assert_eq!(FieldValue::Float(5.0).as_i64(), None);
119    }
120
121    #[test]
122    fn as_u64() {
123        assert_eq!(FieldValue::Uint(123).as_u64(), Some(123));
124        assert_eq!(FieldValue::Int(123).as_u64(), None);
125    }
126
127    #[test]
128    fn as_str() {
129        assert_eq!(FieldValue::String("test".into()).as_str(), Some("test"));
130        assert_eq!(FieldValue::Int(0).as_str(), None);
131    }
132}