timelake_format/
bounds.rs1#[derive(Debug, Clone, PartialEq, PartialOrd)]
6pub enum Bound {
7 Bool(bool),
9 Int(i64),
11 Float(f64),
13 Str(String),
15}
16
17pub fn decode(iceberg_type: &str, bytes: &[u8]) -> Option<Bound> {
20 let le4 = || bytes.first_chunk::<4>().copied();
21 let le8 = || bytes.first_chunk::<8>().copied();
22 Some(match iceberg_type {
23 "boolean" => Bound::Bool(*bytes.first()? != 0),
24 "int" | "date" => Bound::Int(i32::from_le_bytes(le4()?).into()),
25 "long" | "time" | "timestamp" | "timestamptz" | "timestamp_ns" | "timestamptz_ns" => {
26 Bound::Int(i64::from_le_bytes(le8()?))
27 }
28 "float" => Bound::Float(f32::from_le_bytes(le4()?).into()),
29 "double" => Bound::Float(f64::from_le_bytes(le8()?)),
30 "string" => Bound::Str(String::from_utf8(bytes.to_vec()).ok()?),
31 _ => return None,
32 })
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38
39 #[test]
40 fn decodes_primitives() {
41 assert_eq!(decode("int", &7i32.to_le_bytes()), Some(Bound::Int(7)));
42 assert_eq!(decode("date", &(-1i32).to_le_bytes()), Some(Bound::Int(-1)));
43 assert_eq!(
44 decode("timestamptz", &1_700_000_000_000_000i64.to_le_bytes()),
45 Some(Bound::Int(1_700_000_000_000_000))
46 );
47 assert_eq!(decode("double", &1.5f64.to_le_bytes()), Some(Bound::Float(1.5)));
48 assert_eq!(decode("float", &0.5f32.to_le_bytes()), Some(Bound::Float(0.5)));
49 assert_eq!(decode("boolean", &[1]), Some(Bound::Bool(true)));
50 assert_eq!(decode("string", b"truck_1"), Some(Bound::Str("truck_1".into())));
51 assert_eq!(decode("decimal(10,2)", &[0]), None);
52 }
53
54 #[test]
55 fn short_input_is_none() {
56 assert_eq!(decode("long", &[1, 2, 3]), None);
57 assert_eq!(decode("int", &[]), None);
58 assert_eq!(decode("boolean", &[]), None);
59 assert_eq!(decode("string", &[0xff]), None);
60 }
61}