1use serde::{Deserialize, Serialize};
4
5#[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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
75pub struct InternalID {
76 pub table_id: u64,
77 pub offset: u64,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
82pub struct Date(pub i32);
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
86pub struct Timestamp(pub i64);
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
90pub struct TimestampTZ(pub i64);
91
92#[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#[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
135impl 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 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 #[inline(always)]
291 pub fn physical_type(&self) -> PhysicalTypeID {
292 physical_type_from_logical(self.logical_type())
293 }
294}
295
296#[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 #[inline(always)]
334 pub fn from_days_since_epoch(days: i32) -> Self {
335 Date(days)
336 }
337
338 #[inline(always)]
340 pub fn days_since_epoch(&self) -> i32 {
341 self.0
342 }
343}
344
345impl Timestamp {
346 #[inline(always)]
348 pub fn from_micros_since_epoch(micros: i64) -> Self {
349 Timestamp(micros)
350 }
351
352 #[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
365pub fn extract_f64_list(val: &Value) -> Result<Vec<f64>, String> {
371 match val {
372 Value::List(items) => {
373 let mut result = Vec::with_capacity(items.len());
374 for item in items {
375 match item {
376 Value::Double(d) => result.push(*d),
377 Value::Int64(i) => result.push(*i as f64),
378 Value::Int32(i) => result.push(*i as f64),
379 Value::Float(f) => result.push(*f as f64),
380 other => {
381 return Err(format!("Expected numeric value in vector list, got {:?}", other));
382 }
383 }
384 }
385 Ok(result)
386 }
387 other => Err(format!("Expected List value for vector, got {:?}", other)),
388 }
389}
390
391pub fn pk_value_to_string(v: &Value) -> String {
398 match v {
399 Value::Null => "null".to_string(),
400 Value::Bool(b) => b.to_string(),
401 Value::Int64(i) => i.to_string(),
402 Value::Int32(i) => i.to_string(),
403 Value::Int16(i) => i.to_string(),
404 Value::Int8(i) => i.to_string(),
405 Value::UInt64(u) => u.to_string(),
406 Value::UInt32(u) => u.to_string(),
407 Value::UInt16(u) => u.to_string(),
408 Value::UInt8(u) => u.to_string(),
409 Value::Double(f) => f.to_string(),
410 Value::Float(f) => f.to_string(),
411 Value::String(s) => s.clone(),
412 Value::Date(d) => format!("Date({})", d.0),
413 Value::Timestamp(ts) => format!("Timestamp({})", ts.0),
414 other => format!("{other:?}"),
415 }
416}
417
418pub fn value_to_csv_string(v: &Value) -> String {
421 match v {
422 Value::Null => String::new(),
423 other => pk_value_to_string(other),
424 }
425}
426
427#[cfg(test)]
428mod tests {
429 use super::*;
430
431 #[test]
432 fn test_value_from_primitives() {
433 assert_eq!(Value::from(true), Value::Bool(true));
434 assert_eq!(Value::from(42i64), Value::Int64(42));
435 assert_eq!(Value::from(42i32), Value::Int32(42));
436 assert_eq!(Value::from(std::f64::consts::PI), Value::Double(std::f64::consts::PI));
437 assert_eq!(Value::from("hello"), Value::String("hello".into()));
438 assert_eq!(Value::from(Date(100)), Value::Date(Date(100)));
439 }
440
441 #[test]
442 fn test_value_logical_type() {
443 assert_eq!(Value::Null.logical_type(), LogicalTypeID::Any);
444 assert_eq!(Value::Bool(true).logical_type(), LogicalTypeID::Bool);
445 assert_eq!(Value::Int64(1).logical_type(), LogicalTypeID::Int64);
446 assert_eq!(Value::String("a".into()).logical_type(), LogicalTypeID::String);
447 assert_eq!(Value::List(vec![]).logical_type(), LogicalTypeID::List);
448 }
449
450 #[test]
451 fn test_extract_f64_list() {
452 assert_eq!(
453 extract_f64_list(&Value::List(vec![
454 Value::Double(1.5),
455 Value::Int64(2),
456 Value::Int32(3),
457 Value::Float(4.0),
458 ]))
459 .unwrap(),
460 vec![1.5, 2.0, 3.0, 4.0]
461 );
462 assert!(extract_f64_list(&Value::String("x".into())).is_err());
463 assert!(extract_f64_list(&Value::List(vec![Value::Bool(true)])).is_err());
464 assert_eq!(extract_f64_list(&Value::List(vec![])).unwrap(), Vec::<f64>::new());
465 }
466
467 #[test]
468 fn test_physical_type_from_logical() {
469 assert_eq!(physical_type_from_logical(LogicalTypeID::Bool), PhysicalTypeID::Bool);
470 assert_eq!(physical_type_from_logical(LogicalTypeID::Int64), PhysicalTypeID::Int64);
471 assert_eq!(
472 physical_type_from_logical(LogicalTypeID::String),
473 PhysicalTypeID::String
474 );
475 assert_eq!(physical_type_from_logical(LogicalTypeID::Date), PhysicalTypeID::Int64);
476 assert_eq!(physical_type_from_logical(LogicalTypeID::List), PhysicalTypeID::List);
477 }
478
479 #[test]
480 fn test_date_roundtrip() {
481 let d = Date::from_days_since_epoch(20000);
482 assert_eq!(d.days_since_epoch(), 20000);
483 }
484
485 #[test]
486 fn test_timestamp_roundtrip() {
487 let ts = Timestamp::from_micros_since_epoch(1_700_000_000_000_000);
488 assert_eq!(ts.micros_since_epoch(), 1_700_000_000_000_000);
489 }
490
491 #[test]
492 fn test_internal_id() {
493 let id = InternalID {
494 table_id: 5,
495 offset: 100,
496 };
497 assert_eq!(id.table_id, 5);
498 assert_eq!(id.offset, 100);
499 }
500
501 #[test]
502 fn test_value_from_list() {
503 let list = Value::List(vec![Value::Int64(1), Value::Int64(2)]);
504 assert_eq!(list.logical_type(), LogicalTypeID::List);
505 }
506
507 #[test]
508 fn test_value_physical_type() {
509 let v: Value = 42i64.into();
510 assert_eq!(v.physical_type(), PhysicalTypeID::Int64);
511 let v: Value = std::f64::consts::PI.into();
512 assert_eq!(v.physical_type(), PhysicalTypeID::Double);
513 let v: Value = "test".into();
514 assert_eq!(v.physical_type(), PhysicalTypeID::String);
515 }
516
517 #[test]
518 fn test_pk_value_to_string_canonical_keys() {
519 assert_eq!(pk_value_to_string(&Value::Null), "null");
520 assert_eq!(pk_value_to_string(&Value::Bool(true)), "true");
521 assert_eq!(pk_value_to_string(&Value::Int64(-7)), "-7");
522 assert_eq!(pk_value_to_string(&Value::Int32(9)), "9");
523 assert_eq!(pk_value_to_string(&Value::Int16(5)), "5");
526 assert_eq!(pk_value_to_string(&Value::Int8(3)), "3");
527 assert_eq!(pk_value_to_string(&Value::UInt64(u64::MAX)), u64::MAX.to_string());
528 assert_eq!(pk_value_to_string(&Value::UInt32(11)), "11");
529 assert_eq!(pk_value_to_string(&Value::UInt16(12)), "12");
530 assert_eq!(pk_value_to_string(&Value::UInt8(13)), "13");
531 assert_eq!(pk_value_to_string(&Value::Double(1.5)), "1.5");
532 assert_eq!(pk_value_to_string(&Value::Float(0.25)), "0.25");
533 assert_eq!(pk_value_to_string(&Value::String("k".into())), "k");
534 assert_eq!(
535 pk_value_to_string(&Value::Date(Date(20000))),
536 format!("Date({})", 20000)
537 );
538 assert_eq!(
539 pk_value_to_string(&Value::Timestamp(Timestamp(1_700_000_000_000_000))),
540 "Timestamp(1700000000000000)"
541 );
542 assert_eq!(
544 pk_value_to_string(&Value::List(vec![Value::Int64(1)])),
545 "List([Int64(1)])"
546 );
547 }
548
549 #[test]
550 fn test_value_to_csv_string_null_is_empty_cell() {
551 assert_eq!(value_to_csv_string(&Value::Null), "");
552 assert_eq!(value_to_csv_string(&Value::Bool(false)), "false");
553 assert_eq!(value_to_csv_string(&Value::Int64(42)), "42");
554 assert_eq!(value_to_csv_string(&Value::String("a,b".into())), "a,b");
555 assert_eq!(value_to_csv_string(&Value::UInt8(7)), "7");
556 }
557}