Skip to main content

kyu_types/
physical_type.rs

1/// Physical storage representation of a value.
2/// Controls how bytes are laid out in columns and the Value union.
3///
4/// Discriminant values match the C++ `PhysicalTypeID` for on-disk compatibility.
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
6#[repr(u8)]
7pub enum PhysicalType {
8    Bool = 1,
9    Int64 = 2,
10    Int32 = 3,
11    Int16 = 4,
12    Int8 = 5,
13    UInt64 = 6,
14    UInt32 = 7,
15    UInt16 = 8,
16    UInt8 = 9,
17    Int128 = 10,
18    Float64 = 11,
19    Float32 = 12,
20    Interval = 13,
21    InternalId = 14,
22    String = 20,
23    List = 22,
24    Array = 23,
25    Struct = 24,
26}
27
28impl PhysicalType {
29    /// Size in bytes of the fixed-size inline representation.
30    /// Returns `None` for variable-length types (String, List, Struct).
31    pub const fn fixed_size(&self) -> Option<usize> {
32        match self {
33            Self::Bool => Some(1),
34            Self::Int8 | Self::UInt8 => Some(1),
35            Self::Int16 | Self::UInt16 => Some(2),
36            Self::Int32 | Self::UInt32 | Self::Float32 => Some(4),
37            Self::Int64 | Self::UInt64 | Self::Float64 => Some(8),
38            Self::Int128 => Some(16),
39            Self::Interval => Some(16), // months(4) + days(4) + micros(8)
40            Self::InternalId => Some(16), // table_id(8) + offset(8)
41            Self::String => None,
42            Self::List => None,
43            Self::Array => None,
44            Self::Struct => None,
45        }
46    }
47
48    /// Whether this type has a fixed size (stored inline in Value).
49    pub const fn is_fixed_size(&self) -> bool {
50        self.fixed_size().is_some()
51    }
52}
53
54impl std::fmt::Display for PhysicalType {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        let name = match self {
57            Self::Bool => "BOOL",
58            Self::Int8 => "INT8",
59            Self::Int16 => "INT16",
60            Self::Int32 => "INT32",
61            Self::Int64 => "INT64",
62            Self::Int128 => "INT128",
63            Self::UInt8 => "UINT8",
64            Self::UInt16 => "UINT16",
65            Self::UInt32 => "UINT32",
66            Self::UInt64 => "UINT64",
67            Self::Float32 => "FLOAT",
68            Self::Float64 => "DOUBLE",
69            Self::Interval => "INTERVAL",
70            Self::InternalId => "INTERNAL_ID",
71            Self::String => "STRING",
72            Self::List => "LIST",
73            Self::Array => "ARRAY",
74            Self::Struct => "STRUCT",
75        };
76        write!(f, "{name}")
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn discriminant_values_match_cpp() {
86        assert_eq!(PhysicalType::Bool as u8, 1);
87        assert_eq!(PhysicalType::Int64 as u8, 2);
88        assert_eq!(PhysicalType::Int32 as u8, 3);
89        assert_eq!(PhysicalType::Int16 as u8, 4);
90        assert_eq!(PhysicalType::Int8 as u8, 5);
91        assert_eq!(PhysicalType::UInt64 as u8, 6);
92        assert_eq!(PhysicalType::UInt32 as u8, 7);
93        assert_eq!(PhysicalType::UInt16 as u8, 8);
94        assert_eq!(PhysicalType::UInt8 as u8, 9);
95        assert_eq!(PhysicalType::Int128 as u8, 10);
96        assert_eq!(PhysicalType::Float64 as u8, 11);
97        assert_eq!(PhysicalType::Float32 as u8, 12);
98        assert_eq!(PhysicalType::Interval as u8, 13);
99        assert_eq!(PhysicalType::InternalId as u8, 14);
100        assert_eq!(PhysicalType::String as u8, 20);
101        assert_eq!(PhysicalType::List as u8, 22);
102        assert_eq!(PhysicalType::Array as u8, 23);
103        assert_eq!(PhysicalType::Struct as u8, 24);
104    }
105
106    #[test]
107    fn fixed_sizes() {
108        assert_eq!(PhysicalType::Bool.fixed_size(), Some(1));
109        assert_eq!(PhysicalType::Int8.fixed_size(), Some(1));
110        assert_eq!(PhysicalType::Int16.fixed_size(), Some(2));
111        assert_eq!(PhysicalType::Int32.fixed_size(), Some(4));
112        assert_eq!(PhysicalType::Int64.fixed_size(), Some(8));
113        assert_eq!(PhysicalType::Int128.fixed_size(), Some(16));
114        assert_eq!(PhysicalType::Float32.fixed_size(), Some(4));
115        assert_eq!(PhysicalType::Float64.fixed_size(), Some(8));
116        assert_eq!(PhysicalType::Interval.fixed_size(), Some(16));
117        assert_eq!(PhysicalType::InternalId.fixed_size(), Some(16));
118        assert_eq!(PhysicalType::String.fixed_size(), None);
119        assert_eq!(PhysicalType::List.fixed_size(), None);
120        assert_eq!(PhysicalType::Array.fixed_size(), None);
121        assert_eq!(PhysicalType::Struct.fixed_size(), None);
122    }
123
124    #[test]
125    fn is_fixed_size() {
126        assert!(PhysicalType::Int64.is_fixed_size());
127        assert!(!PhysicalType::String.is_fixed_size());
128        assert!(!PhysicalType::List.is_fixed_size());
129    }
130
131    #[test]
132    fn enum_is_one_byte() {
133        assert_eq!(std::mem::size_of::<PhysicalType>(), 1);
134    }
135
136    #[test]
137    fn display() {
138        assert_eq!(PhysicalType::Bool.to_string(), "BOOL");
139        assert_eq!(PhysicalType::String.to_string(), "STRING");
140        assert_eq!(PhysicalType::InternalId.to_string(), "INTERNAL_ID");
141    }
142}