Skip to main content

akar_common/
types.rs

1//! Core type system: LogicalType, PhysicalType, Value, InternalID, date/time types.
2
3use serde::{Deserialize, Serialize};
4
5/// Logical type identifiers for Akar's type system.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
7#[repr(u8)]
8pub enum LogicalTypeID {
9    Any = 0,
10    Node = 10,
11    Rel = 11,
12    RecursiveRel = 12,
13    Serial = 13,
14    Bool = 22,
15    Int64 = 23,
16    Int32 = 24,
17    Int16 = 25,
18    Int8 = 26,
19    UInt64 = 27,
20    UInt32 = 28,
21    UInt16 = 29,
22    UInt8 = 30,
23    Int128 = 31,
24    Double = 32,
25    Float = 33,
26    Date = 34,
27    Timestamp = 35,
28    TimestampSec = 36,
29    TimestampMs = 37,
30    TimestampNs = 38,
31    TimestampTz = 39,
32    Interval = 40,
33    Decimal = 41,
34    InternalID = 42,
35    UInt128 = 43,
36    Json = 44,
37    Time = 45,
38    String = 50,
39    Blob = 51,
40    List = 52,
41    Array = 53,
42    Struct = 54,
43    Map = 55,
44    Union = 56,
45    Uuid = 59,
46}
47
48/// Physical type identifiers for in-memory representation.
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
50#[repr(u8)]
51pub enum PhysicalTypeID {
52    Any = 0,
53    Bool = 1,
54    Int64 = 2,
55    Int32 = 3,
56    Int16 = 4,
57    Int8 = 5,
58    UInt64 = 6,
59    UInt32 = 7,
60    UInt16 = 8,
61    UInt8 = 9,
62    Int128 = 10,
63    Double = 11,
64    Float = 12,
65    Interval = 13,
66    String = 14,
67    Struct = 15,
68    List = 16,
69    Array = 17,
70    Blob = 20,
71}
72
73/// A 4-byte aligned, 8-byte internal node/rel identifier.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
75pub struct InternalID {
76    pub table_id: u64,
77    pub offset: u64,
78}
79
80/// Date representation (days since epoch).
81#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
82pub struct Date(pub i32);
83
84/// Timestamp representation (microseconds since epoch).
85#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
86pub struct Timestamp(pub i64);
87
88/// Timestamp with timezone.
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
90pub struct TimestampTZ(pub i64);
91
92/// Interval (duration).
93#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
94pub struct Interval {
95    pub months: i32,
96    pub days: i32,
97    pub micros: i64,
98}
99
100/// A Akar value — the runtime representation of any data type.
101#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
102pub enum Value {
103    Null,
104    Bool(bool),
105    Int64(i64),
106    Int32(i32),
107    Int16(i16),
108    Int8(i8),
109    UInt64(u64),
110    UInt32(u32),
111    UInt16(u16),
112    UInt8(u8),
113    Int128(i128),
114    Double(f64),
115    Float(f32),
116    String(String),
117    Blob(Vec<u8>),
118    Date(Date),
119    Timestamp(Timestamp),
120    TimestampTz(TimestampTZ),
121    TimestampNs(Timestamp),
122    TimestampMs(Timestamp),
123    TimestampSec(Timestamp),
124    Interval(Interval),
125    InternalID(InternalID),
126    UInt128(u128),
127    Json(serde_json::Value),
128    DTime(i64),
129    Union(String, Box<Value>),
130    List(Vec<Value>),
131    Map(Vec<(Value, Value)>),
132    Struct(Vec<(String, Value)>),
133}
134
135// --- From implementations for Value ---
136
137impl From<bool> for Value {
138    #[inline(always)]
139    fn from(v: bool) -> Self {
140        Value::Bool(v)
141    }
142}
143impl From<i64> for Value {
144    #[inline(always)]
145    fn from(v: i64) -> Self {
146        Value::Int64(v)
147    }
148}
149impl From<i32> for Value {
150    #[inline(always)]
151    fn from(v: i32) -> Self {
152        Value::Int32(v)
153    }
154}
155impl From<i16> for Value {
156    #[inline(always)]
157    fn from(v: i16) -> Self {
158        Value::Int16(v)
159    }
160}
161impl From<i8> for Value {
162    #[inline(always)]
163    fn from(v: i8) -> Self {
164        Value::Int8(v)
165    }
166}
167impl From<u64> for Value {
168    #[inline(always)]
169    fn from(v: u64) -> Self {
170        Value::UInt64(v)
171    }
172}
173impl From<u32> for Value {
174    #[inline(always)]
175    fn from(v: u32) -> Self {
176        Value::UInt32(v)
177    }
178}
179impl From<u16> for Value {
180    #[inline(always)]
181    fn from(v: u16) -> Self {
182        Value::UInt16(v)
183    }
184}
185impl From<u8> for Value {
186    #[inline(always)]
187    fn from(v: u8) -> Self {
188        Value::UInt8(v)
189    }
190}
191impl From<f64> for Value {
192    #[inline(always)]
193    fn from(v: f64) -> Self {
194        Value::Double(v)
195    }
196}
197impl From<f32> for Value {
198    #[inline(always)]
199    fn from(v: f32) -> Self {
200        Value::Float(v)
201    }
202}
203impl From<String> for Value {
204    #[inline(always)]
205    fn from(v: String) -> Self {
206        Value::String(v)
207    }
208}
209impl From<&str> for Value {
210    #[inline(always)]
211    fn from(v: &str) -> Self {
212        Value::String(v.to_string())
213    }
214}
215impl From<Date> for Value {
216    #[inline(always)]
217    fn from(v: Date) -> Self {
218        Value::Date(v)
219    }
220}
221impl From<Timestamp> for Value {
222    #[inline(always)]
223    fn from(v: Timestamp) -> Self {
224        Value::Timestamp(v)
225    }
226}
227impl From<Interval> for Value {
228    #[inline(always)]
229    fn from(v: Interval) -> Self {
230        Value::Interval(v)
231    }
232}
233impl From<InternalID> for Value {
234    #[inline(always)]
235    fn from(v: InternalID) -> Self {
236        Value::InternalID(v)
237    }
238}
239impl From<u128> for Value {
240    #[inline(always)]
241    fn from(v: u128) -> Self {
242        Value::UInt128(v)
243    }
244}
245impl From<serde_json::Value> for Value {
246    #[inline(always)]
247    fn from(v: serde_json::Value) -> Self {
248        Value::Json(v)
249    }
250}
251
252impl Value {
253    /// Get the LogicalTypeID corresponding to this Value.
254    pub fn logical_type(&self) -> LogicalTypeID {
255        match self {
256            Value::Null => LogicalTypeID::Any,
257            Value::Bool(_) => LogicalTypeID::Bool,
258            Value::Int64(_) => LogicalTypeID::Int64,
259            Value::Int32(_) => LogicalTypeID::Int32,
260            Value::Int16(_) => LogicalTypeID::Int16,
261            Value::Int8(_) => LogicalTypeID::Int8,
262            Value::UInt64(_) => LogicalTypeID::UInt64,
263            Value::UInt32(_) => LogicalTypeID::UInt32,
264            Value::UInt16(_) => LogicalTypeID::UInt16,
265            Value::UInt8(_) => LogicalTypeID::UInt8,
266            Value::Double(_) => LogicalTypeID::Double,
267            Value::Float(_) => LogicalTypeID::Float,
268            Value::String(_) => LogicalTypeID::String,
269            Value::Blob(_) => LogicalTypeID::Blob,
270            Value::Date(_) => LogicalTypeID::Date,
271            Value::Timestamp(_) => LogicalTypeID::Timestamp,
272            Value::Interval(_) => LogicalTypeID::Interval,
273            Value::InternalID(_) => LogicalTypeID::InternalID,
274            Value::UInt128(_) => LogicalTypeID::UInt128,
275            Value::Json(_) => LogicalTypeID::Json,
276            Value::DTime(_) => LogicalTypeID::Time,
277            Value::Union(_, _) => LogicalTypeID::Union,
278            Value::List(_) => LogicalTypeID::List,
279            Value::Map(_) => LogicalTypeID::Map,
280            Value::Struct(_) => LogicalTypeID::Struct,
281            Value::Int128(_) => LogicalTypeID::Int128,
282            Value::TimestampTz(_) => LogicalTypeID::TimestampTz,
283            Value::TimestampNs(_) => LogicalTypeID::TimestampNs,
284            Value::TimestampMs(_) => LogicalTypeID::TimestampMs,
285            Value::TimestampSec(_) => LogicalTypeID::TimestampSec,
286        }
287    }
288
289    /// Get the PhysicalTypeID for this Value's logical type.
290    #[inline(always)]
291    pub fn physical_type(&self) -> PhysicalTypeID {
292        physical_type_from_logical(self.logical_type())
293    }
294}
295
296/// Map a LogicalTypeID to its corresponding PhysicalTypeID.
297#[inline]
298pub const fn physical_type_from_logical(logical: LogicalTypeID) -> PhysicalTypeID {
299    match logical {
300        LogicalTypeID::Any => PhysicalTypeID::Any,
301        LogicalTypeID::Bool => PhysicalTypeID::Bool,
302        LogicalTypeID::Int64 | LogicalTypeID::Serial => PhysicalTypeID::Int64,
303        LogicalTypeID::Int32 => PhysicalTypeID::Int32,
304        LogicalTypeID::Int16 => PhysicalTypeID::Int16,
305        LogicalTypeID::Int8 => PhysicalTypeID::Int8,
306        LogicalTypeID::UInt64 => PhysicalTypeID::UInt64,
307        LogicalTypeID::UInt32 => PhysicalTypeID::UInt32,
308        LogicalTypeID::UInt16 => PhysicalTypeID::UInt16,
309        LogicalTypeID::UInt8 => PhysicalTypeID::UInt8,
310        LogicalTypeID::Double => PhysicalTypeID::Double,
311        LogicalTypeID::Float => PhysicalTypeID::Float,
312        LogicalTypeID::Int128 | LogicalTypeID::Decimal | LogicalTypeID::UInt128 => PhysicalTypeID::Int128,
313        LogicalTypeID::Date
314        | LogicalTypeID::Timestamp
315        | LogicalTypeID::TimestampSec
316        | LogicalTypeID::TimestampMs
317        | LogicalTypeID::TimestampNs
318        | LogicalTypeID::TimestampTz
319        | LogicalTypeID::Time => PhysicalTypeID::Int64,
320        LogicalTypeID::Interval => PhysicalTypeID::Interval,
321        LogicalTypeID::String | LogicalTypeID::Blob | LogicalTypeID::Uuid | LogicalTypeID::Json => {
322            PhysicalTypeID::String
323        }
324        LogicalTypeID::InternalID => PhysicalTypeID::Struct,
325        LogicalTypeID::List | LogicalTypeID::Array => PhysicalTypeID::List,
326        LogicalTypeID::Map | LogicalTypeID::Struct | LogicalTypeID::Union => PhysicalTypeID::Struct,
327        LogicalTypeID::Node | LogicalTypeID::Rel | LogicalTypeID::RecursiveRel => PhysicalTypeID::Struct,
328    }
329}
330
331impl Date {
332    /// Create a Date from epoch days.
333    #[inline(always)]
334    pub fn from_days_since_epoch(days: i32) -> Self {
335        Date(days)
336    }
337
338    /// Get the days since epoch.
339    #[inline(always)]
340    pub fn days_since_epoch(&self) -> i32 {
341        self.0
342    }
343}
344
345impl Timestamp {
346    /// Create a Timestamp from epoch microseconds.
347    #[inline(always)]
348    pub fn from_micros_since_epoch(micros: i64) -> Self {
349        Timestamp(micros)
350    }
351
352    /// Get the microseconds since epoch.
353    #[inline(always)]
354    pub fn micros_since_epoch(&self) -> i64 {
355        self.0
356    }
357}
358
359impl Interval {
360    pub fn new(months: i32, days: i32, micros: i64) -> Self {
361        Interval { months, days, micros }
362    }
363}
364
365#[cfg(test)]
366mod tests {
367    use super::*;
368
369    #[test]
370    fn test_value_from_primitives() {
371        assert_eq!(Value::from(true), Value::Bool(true));
372        assert_eq!(Value::from(42i64), Value::Int64(42));
373        assert_eq!(Value::from(42i32), Value::Int32(42));
374        assert_eq!(Value::from(std::f64::consts::PI), Value::Double(std::f64::consts::PI));
375        assert_eq!(Value::from("hello"), Value::String("hello".into()));
376        assert_eq!(Value::from(Date(100)), Value::Date(Date(100)));
377    }
378
379    #[test]
380    fn test_value_logical_type() {
381        assert_eq!(Value::Null.logical_type(), LogicalTypeID::Any);
382        assert_eq!(Value::Bool(true).logical_type(), LogicalTypeID::Bool);
383        assert_eq!(Value::Int64(1).logical_type(), LogicalTypeID::Int64);
384        assert_eq!(Value::String("a".into()).logical_type(), LogicalTypeID::String);
385        assert_eq!(Value::List(vec![]).logical_type(), LogicalTypeID::List);
386    }
387
388    #[test]
389    fn test_physical_type_from_logical() {
390        assert_eq!(physical_type_from_logical(LogicalTypeID::Bool), PhysicalTypeID::Bool);
391        assert_eq!(physical_type_from_logical(LogicalTypeID::Int64), PhysicalTypeID::Int64);
392        assert_eq!(
393            physical_type_from_logical(LogicalTypeID::String),
394            PhysicalTypeID::String
395        );
396        assert_eq!(physical_type_from_logical(LogicalTypeID::Date), PhysicalTypeID::Int64);
397        assert_eq!(physical_type_from_logical(LogicalTypeID::List), PhysicalTypeID::List);
398    }
399
400    #[test]
401    fn test_date_roundtrip() {
402        let d = Date::from_days_since_epoch(20000);
403        assert_eq!(d.days_since_epoch(), 20000);
404    }
405
406    #[test]
407    fn test_timestamp_roundtrip() {
408        let ts = Timestamp::from_micros_since_epoch(1_700_000_000_000_000);
409        assert_eq!(ts.micros_since_epoch(), 1_700_000_000_000_000);
410    }
411
412    #[test]
413    fn test_internal_id() {
414        let id = InternalID {
415            table_id: 5,
416            offset: 100,
417        };
418        assert_eq!(id.table_id, 5);
419        assert_eq!(id.offset, 100);
420    }
421
422    #[test]
423    fn test_value_from_list() {
424        let list = Value::List(vec![Value::Int64(1), Value::Int64(2)]);
425        assert_eq!(list.logical_type(), LogicalTypeID::List);
426    }
427
428    #[test]
429    fn test_value_physical_type() {
430        let v: Value = 42i64.into();
431        assert_eq!(v.physical_type(), PhysicalTypeID::Int64);
432        let v: Value = std::f64::consts::PI.into();
433        assert_eq!(v.physical_type(), PhysicalTypeID::Double);
434        let v: Value = "test".into();
435        assert_eq!(v.physical_type(), PhysicalTypeID::String);
436    }
437}