pcapsql_core/schema/
kind.rs

1//! Engine-agnostic data type definitions.
2
3/// Data types that can be represented in any columnar format.
4///
5/// These map to:
6/// - Arrow: `DataType::*`
7/// - DuckDB: `LogicalType::*`
8/// - Parquet: Physical + Logical types
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10pub enum DataKind {
11    /// Boolean (true/false)
12    Bool,
13
14    /// Unsigned 8-bit integer
15    UInt8,
16
17    /// Unsigned 16-bit integer
18    UInt16,
19
20    /// Unsigned 32-bit integer (also used for IPv4 addresses)
21    UInt32,
22
23    /// Unsigned 64-bit integer
24    UInt64,
25
26    /// Signed 64-bit integer
27    Int64,
28
29    /// 64-bit floating point
30    Float64,
31
32    /// UTF-8 string
33    String,
34
35    /// Variable-length binary data
36    Binary,
37
38    /// Fixed-size binary data (e.g., MAC address = 6, IPv6 = 16)
39    FixedBinary(usize),
40
41    /// Timestamp with microsecond precision (UTC)
42    TimestampMicros,
43
44    /// Variable-length list of elements of the same type
45    List(Box<DataKind>),
46}
47
48impl DataKind {
49    /// Human-readable type name for display.
50    pub fn type_name(&self) -> &'static str {
51        match self {
52            DataKind::Bool => "bool",
53            DataKind::UInt8 => "u8",
54            DataKind::UInt16 => "u16",
55            DataKind::UInt32 => "u32",
56            DataKind::UInt64 => "u64",
57            DataKind::Int64 => "i64",
58            DataKind::Float64 => "f64",
59            DataKind::String => "string",
60            DataKind::Binary => "binary",
61            DataKind::FixedBinary(n) => match n {
62                6 => "mac",
63                16 => "ipv6",
64                _ => "fixed_binary",
65            },
66            DataKind::TimestampMicros => "timestamp",
67            DataKind::List(_) => "list",
68        }
69    }
70
71    /// Size in bytes for fixed-width types, None for variable-width.
72    pub fn fixed_size(&self) -> Option<usize> {
73        match self {
74            DataKind::Bool => Some(1),
75            DataKind::UInt8 => Some(1),
76            DataKind::UInt16 => Some(2),
77            DataKind::UInt32 => Some(4),
78            DataKind::UInt64 => Some(8),
79            DataKind::Int64 => Some(8),
80            DataKind::Float64 => Some(8),
81            DataKind::TimestampMicros => Some(8),
82            DataKind::FixedBinary(n) => Some(*n),
83            DataKind::String | DataKind::Binary | DataKind::List(_) => None,
84        }
85    }
86
87    /// Get the inner type for List, or None if not a List.
88    pub fn list_inner(&self) -> Option<&DataKind> {
89        match self {
90            DataKind::List(inner) => Some(inner),
91            _ => None,
92        }
93    }
94}
95
96impl std::fmt::Display for DataKind {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        write!(f, "{}", self.type_name())
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_type_names() {
108        assert_eq!(DataKind::Bool.type_name(), "bool");
109        assert_eq!(DataKind::UInt32.type_name(), "u32");
110        assert_eq!(DataKind::String.type_name(), "string");
111        assert_eq!(DataKind::FixedBinary(6).type_name(), "mac");
112        assert_eq!(DataKind::FixedBinary(16).type_name(), "ipv6");
113    }
114
115    #[test]
116    fn test_fixed_sizes() {
117        assert_eq!(DataKind::UInt32.fixed_size(), Some(4));
118        assert_eq!(DataKind::String.fixed_size(), None);
119        assert_eq!(DataKind::FixedBinary(6).fixed_size(), Some(6));
120    }
121
122    #[test]
123    fn test_list_type() {
124        let list_u32 = DataKind::List(Box::new(DataKind::UInt32));
125        assert_eq!(list_u32.type_name(), "list");
126        assert_eq!(list_u32.fixed_size(), None);
127        assert_eq!(list_u32.list_inner(), Some(&DataKind::UInt32));
128
129        // Nested list
130        let list_list = DataKind::List(Box::new(DataKind::List(Box::new(DataKind::String))));
131        assert_eq!(list_list.type_name(), "list");
132        assert!(matches!(list_list.list_inner(), Some(DataKind::List(_))));
133    }
134}