Skip to main content

geonative_core/
value.rs

1//! Attribute values and their type tags.
2//!
3//! `Value` is the per-cell payload; `ValueType` is the discriminant used in
4//! [`crate::FieldDef`] to declare what type a column holds.
5//!
6//! ## DateTime
7//!
8//! v0.1 stores datetimes as `f64` **days since 1899-12-30 00:00:00**, matching
9//! the raw GDB encoding. This is lossless and zero-dep. Writers convert to
10//! their preferred representation (ISO 8601, Arrow Timestamp, …) at write time.
11//! A strong-typed `DateTime` value is on the roadmap for v0.2.
12
13#[derive(Debug, Clone, PartialEq)]
14pub enum Value {
15    Null,
16    Bool(bool),
17    Int16(i16),
18    Int32(i32),
19    Int64(i64),
20    Float32(f32),
21    Float64(f64),
22    String(String),
23    Binary(Vec<u8>),
24    /// Days since 1899-12-30 00:00:00 (Esri convention). See module docs.
25    DateTime(f64),
26    /// 16-byte UUID, raw bytes (no string formatting).
27    Guid([u8; 16]),
28    /// XML payload, stored verbatim.
29    Xml(String),
30}
31
32/// Type discriminant for [`Value`]. Used by [`crate::FieldDef`] to describe
33/// schema without binding a per-cell value.
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub enum ValueType {
36    Bool,
37    Int16,
38    Int32,
39    Int64,
40    Float32,
41    Float64,
42    String,
43    Binary,
44    DateTime,
45    Guid,
46    Xml,
47}
48
49impl Value {
50    /// Returns `None` for `Value::Null`, otherwise the type of the variant.
51    pub fn ty(&self) -> Option<ValueType> {
52        Some(match self {
53            Value::Null => return None,
54            Value::Bool(_) => ValueType::Bool,
55            Value::Int16(_) => ValueType::Int16,
56            Value::Int32(_) => ValueType::Int32,
57            Value::Int64(_) => ValueType::Int64,
58            Value::Float32(_) => ValueType::Float32,
59            Value::Float64(_) => ValueType::Float64,
60            Value::String(_) => ValueType::String,
61            Value::Binary(_) => ValueType::Binary,
62            Value::DateTime(_) => ValueType::DateTime,
63            Value::Guid(_) => ValueType::Guid,
64            Value::Xml(_) => ValueType::Xml,
65        })
66    }
67
68    pub fn is_null(&self) -> bool {
69        matches!(self, Value::Null)
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn ty_returns_none_for_null() {
79        assert_eq!(Value::Null.ty(), None);
80        assert!(Value::Null.is_null());
81    }
82
83    #[test]
84    fn ty_matches_variant() {
85        assert_eq!(Value::Bool(true).ty(), Some(ValueType::Bool));
86        assert_eq!(Value::Int16(1).ty(), Some(ValueType::Int16));
87        assert_eq!(Value::Int32(1).ty(), Some(ValueType::Int32));
88        assert_eq!(Value::Int64(1).ty(), Some(ValueType::Int64));
89        assert_eq!(Value::Float32(1.0).ty(), Some(ValueType::Float32));
90        assert_eq!(Value::Float64(1.0).ty(), Some(ValueType::Float64));
91        assert_eq!(Value::String("x".into()).ty(), Some(ValueType::String));
92        assert_eq!(Value::Binary(vec![]).ty(), Some(ValueType::Binary));
93        assert_eq!(Value::DateTime(0.0).ty(), Some(ValueType::DateTime));
94        assert_eq!(Value::Guid([0; 16]).ty(), Some(ValueType::Guid));
95        assert_eq!(Value::Xml("<x/>".into()).ty(), Some(ValueType::Xml));
96        assert!(!Value::Int32(1).is_null());
97    }
98}