use std::hash::{Hash, Hasher};
use serde::{Serialize, Serializer, ser::SerializeMap};
use xxhash_rust::xxh3::Xxh3;
use crate::scalar::ScalarTyp;
pub type NamedField = (&'static str, Typ);
pub type Arr<T> = &'static [T];
pub type Str = &'static str;
pub type Map<T> = Arr<(u8, T)>;
#[derive(PartialEq, Clone, Serialize, Debug, Copy, Hash)]
#[serde(tag = "type", content = "data")]
pub enum Fields {
Named(Arr<NamedField>),
Unnamed(Arr<Typ>),
}
#[derive(PartialEq, Clone, Serialize, Debug, Copy, Hash)]
pub struct StructType {
pub name: &'static str,
pub fields: Fields,
}
fn variants<S, T>(variants: Map<T>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Serialize,
{
let mut map = serializer.serialize_map(Some(variants.len()))?;
for (key, value) in variants {
map.serialize_entry(key, value)?;
}
map.end()
}
#[derive(PartialEq, Clone, Serialize, Debug, Copy, Hash)]
pub struct EnumType {
pub name: Str,
#[serde(serialize_with = "variants")]
pub variants: Map<NamedField>,
}
#[derive(PartialEq, Eq, Clone, Serialize, Debug, Copy, Hash)]
pub struct SimpleEnumType {
pub name: Str,
#[serde(serialize_with = "variants")]
pub variants: Map<Str>,
}
#[derive(PartialEq, Clone, Debug, Copy, Hash)]
pub enum Typ {
Scalar(ScalarTyp),
Array(u32, &'static Typ),
Vec(&'static Typ),
Optional(&'static Typ),
SimpleEnum(SimpleEnumType),
Struct(StructType),
Enum(EnumType),
Custom(&'static str, &'static [Typ]),
}
#[derive(Serialize)]
#[serde(tag = "type", content = "data")]
enum TypComposite {
Array(u32, &'static Typ),
Vec(&'static Typ),
Optional(&'static Typ),
SimpleEnum(SimpleEnumType),
Struct(StructType),
Enum(EnumType),
Custom(&'static str, &'static [Typ]),
}
impl Serialize for Typ {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Typ::Scalar(s) => s.serialize(serializer),
Typ::Array(n, inner) => TypComposite::Array(*n, inner).serialize(serializer),
Typ::Vec(inner) => TypComposite::Vec(inner).serialize(serializer),
Typ::Optional(inner) => TypComposite::Optional(inner).serialize(serializer),
Typ::SimpleEnum(e) => TypComposite::SimpleEnum(*e).serialize(serializer),
Typ::Struct(s) => TypComposite::Struct(*s).serialize(serializer),
Typ::Enum(e) => TypComposite::Enum(*e).serialize(serializer),
Typ::Custom(name, args) => TypComposite::Custom(name, args).serialize(serializer),
}
}
}
impl Typ {
pub fn h(&self) -> u64 {
let mut hasher = Xxh3::new();
self.hash(&mut hasher);
hasher.finish()
}
}
#[cfg(test)]
mod golden {
use super::*;
use crate::scalar::ScalarTyp;
fn golden_cases() -> Vec<(&'static str, Typ, u64)> {
use ScalarTyp::*;
vec![
("Bool", Typ::Scalar(Bool), 0xd0a66a65c7528968),
("U8", Typ::Scalar(U8), 0x0cfbaea74a8cc0e5),
("U16", Typ::Scalar(U16), 0x59ea21c711c350c2),
("U32", Typ::Scalar(U32), 0xa9c537f75b27a974),
("U64", Typ::Scalar(U64), 0xeb8a271e1e70f1b5),
("I32", Typ::Scalar(I32), 0xace144d934729048),
("I64", Typ::Scalar(I64), 0x280869ef8929d9c9),
("F32", Typ::Scalar(F32), 0xa19e70708438a272),
("F64", Typ::Scalar(F64), 0x843be9b447f64cd1),
("Str", Typ::Scalar(Str), 0xf372327de2e0d2a4),
("Datetime", Typ::Scalar(Datetime), 0x54672872443b2b9b),
("Timestamp", Typ::Scalar(Timestamp), 0xf41318623c6f44b3),
("Decimal", Typ::Scalar(Decimal), 0x04d54e82812b9535),
("Id32", Typ::Scalar(Id32), 0xa1254b871fc40f7c),
("Id64", Typ::Scalar(Id64), 0x08b62024788c9438),
("Fuid", Typ::Scalar(Fuid), 0x6d2bac21c16d425f),
("LowId", Typ::Scalar(LowId), 0xb4e78e7417625641),
("Bytes", Typ::Scalar(Bytes), 0xaeb2d89f37dc9398),
("Void", Typ::Scalar(Void), 0xebae81600548f719),
("RustJson", Typ::Scalar(RustJson), 0x4843696f209475a3),
("JsonBytes", Typ::Scalar(JsonBytes), 0x32c39c1af3fd6a18),
("ArrayBytes", Typ::Scalar(ArrayBytes(4)), 0xdd59821af9eecd6b),
(
"Array",
Typ::Array(3, &Typ::Scalar(U64)),
0xd03c6458e1eab738,
),
("Vec", Typ::Vec(&Typ::Scalar(U64)), 0x0b478fcad27d8bf8),
(
"Optional",
Typ::Optional(&Typ::Scalar(U64)),
0xb5c98357ef64570e,
),
(
"SimpleEnum",
Typ::SimpleEnum(SimpleEnumType {
name: "SE",
variants: &[(0, "A"), (1, "B")],
}),
0xb604588033cf6cbf,
),
(
"StructNamed",
Typ::Struct(StructType {
name: "S",
fields: Fields::Named(&[("a", Typ::Scalar(U64)), ("b", Typ::Scalar(Str))]),
}),
0x34afafe7990e4fe5,
),
(
"StructUnnamed",
Typ::Struct(StructType {
name: "",
fields: Fields::Unnamed(&[Typ::Scalar(U64), Typ::Scalar(Bool)]),
}),
0x50bb6b127532a167,
),
(
"Enum",
Typ::Enum(EnumType {
name: "E",
variants: &[(0, ("A", Typ::Scalar(U64))), (1, ("B", Typ::Scalar(Bool)))],
}),
0xf06c341aa43fa98a,
),
(
"Custom",
Typ::Custom("C", &[Typ::Scalar(U64), Typ::Scalar(Str)]),
0xb9d53c9e69fcd4c5,
),
]
}
#[test]
#[ignore]
fn print_golden() {
for (name, t, _) in golden_cases() {
println!("(\"{name}\", …, 0x{:016x}),", t.h());
}
}
#[test]
fn golden_typ_hash() {
for (name, t, expected) in golden_cases() {
assert_eq!(t.h(), expected, "typ_hash drift for variant `{name}`");
}
}
#[allow(dead_code)]
fn _scalar_guard(s: &ScalarTyp) {
use ScalarTyp::*;
match s {
Bool | U8 | U16 | U32 | U64 | I32 | I64 | F32 | F64 | Str | Datetime | Timestamp
| Decimal | Id32 | Id64 | Fuid | LowId | Bytes | Void | RustJson | JsonBytes => {}
ArrayBytes(_) => {}
}
}
#[allow(dead_code)]
fn _typ_guard(t: &Typ) {
match t {
Typ::Scalar(_) => {}
Typ::Array(_, _) => {}
Typ::Vec(_) => {}
Typ::Optional(_) => {}
Typ::SimpleEnum(_) => {}
Typ::Struct(_) => {}
Typ::Enum(_) => {}
Typ::Custom(_, _) => {}
}
}
}