///|
/// Primitive types defined by the Apache Avro data model.
pub(all) enum Primitive {
Null
Boolean
Int
Long
Float
Double
Bytes
String
} derive(Debug, Eq)
///|
/// A field in an Avro record declaration.
pub(all) struct Field {
name : String
schema : Schema
default_value : Json?
aliases : Array[String]
doc : String?
} derive(Debug, Eq)
///|
pub fn Field::name(self : Field) -> String {
self.name
}
///|
pub fn Field::schema(self : Field) -> Schema {
self.schema
}
///|
pub fn Field::default_value(self : Field) -> Json? {
self.default_value
}
///|
pub fn Field::aliases(self : Field) -> Array[String] {
self.aliases
}
///|
/// A named definition retained by the root schema for reference resolution.
pub(all) struct NamedDefinition {
name : String
kind : SchemaKind
} derive(Debug, Eq)
///|
/// The complete Avro schema algebra. `Named` is a resolved full-name
/// reference; use `Schema::resolve_named` on the root schema to inspect its
/// declaration.
pub(all) enum SchemaKind {
Primitive(Primitive)
Record(
name~ : String,
namespace_~ : String?,
aliases~ : Array[String],
fields~ : Array[Field]
)
Enum(
name~ : String,
namespace_~ : String?,
aliases~ : Array[String],
symbols~ : Array[String],
default_symbol~ : String?
)
Array(items~ : Schema)
Map(values~ : Schema)
Union(branches~ : Array[Schema])
Fixed(
name~ : String,
namespace_~ : String?,
aliases~ : Array[String],
size~ : Int
)
Named(String)
} derive(Debug, Eq)
///|
/// Parsed Avro schema with its source retained for diagnostics and canonical
/// form generation.
pub struct Schema {
kind : SchemaKind
source : String
definitions : Array[NamedDefinition]
} derive(Debug, Eq)
///|
pub fn Schema::new(kind : SchemaKind, source : String) -> Schema {
{ kind, source, definitions: [] }
}
///|
pub fn Schema::kind(self : Schema) -> SchemaKind {
self.kind
}
///|
pub fn Schema::source(self : Schema) -> String {
self.source
}
///|
/// Resolve a full or declared alias name from this parsed root schema.
pub fn Schema::resolve_named(self : Schema, name : String) -> SchemaKind? {
for definition in self.definitions {
if definition.name == name {
return Some(definition.kind)
}
match definition.kind {
Record(aliases~, ..) | Enum(aliases~, ..) | Fixed(aliases~, ..) =>
if aliases.contains(name) {
return Some(definition.kind)
}
_ => ()
}
}
None
}
///|
/// Return the full names declared by this parsed root schema.
pub fn Schema::definition_names(self : Schema) -> Array[String] {
self.definitions.map(definition => definition.name)
}
///|
pub fn Schema::kind_name(self : Schema) -> String {
match self.kind {
Primitive(Null) => "null"
Primitive(Boolean) => "boolean"
Primitive(Int) => "int"
Primitive(Long) => "long"
Primitive(Float) => "float"
Primitive(Double) => "double"
Primitive(Bytes) => "bytes"
Primitive(String) => "string"
Record(name=_, namespace_=_, aliases=_, fields=_) => "record"
Enum(name=_, namespace_=_, aliases=_, symbols=_, default_symbol=_) => "enum"
Array(items=_) => "array"
Map(values=_) => "map"
Union(branches=_) => "union"
Fixed(name=_, namespace_=_, aliases=_, size=_) => "fixed"
Named(name) => name
}
}
///|
fn json_string(value : String) -> String {
Json::string(value).stringify()
}
///|
fn canonical_fields(fields : Array[Field]) -> String {
fields
.map(field => {
"{\"name\":\{json_string(field.name)},\"type\":\{canonical_kind(field.schema.kind)}}"
})
.join(",")
}
///|
fn canonical_kind(kind : SchemaKind) -> String {
match kind {
Primitive(Null) => "\"null\""
Primitive(Boolean) => "\"boolean\""
Primitive(Int) => "\"int\""
Primitive(Long) => "\"long\""
Primitive(Float) => "\"float\""
Primitive(Double) => "\"double\""
Primitive(Bytes) => "\"bytes\""
Primitive(String) => "\"string\""
Record(name~, namespace_=_, aliases=_, fields~) =>
"{\"name\":\{json_string(name)},\"type\":\"record\",\"fields\":[\{canonical_fields(fields)}]}"
Enum(name~, namespace_=_, aliases=_, symbols~, default_symbol=_) => {
let rendered_symbols = symbols.map(json_string).join(",")
"{\"name\":\{json_string(name)},\"type\":\"enum\",\"symbols\":[\{rendered_symbols}]}"
}
Array(items~) =>
"{\"type\":\"array\",\"items\":\{canonical_kind(items.kind)}}"
Map(values~) =>
"{\"type\":\"map\",\"values\":\{canonical_kind(values.kind)}}"
Union(branches~) =>
"[\{branches.map(branch => canonical_kind(branch.kind)).join(",")}]"
Fixed(name~, namespace_=_, aliases=_, size~) =>
"{\"name\":\{json_string(name)},\"type\":\"fixed\",\"size\":\{size}}"
Named(name) => json_string(name)
}
}
///|
/// Return Avro parsing canonical form (PCF) for this schema.
pub fn Schema::canonical_form(self : Schema) -> String {
canonical_kind(self.kind)
}
///|
/// Compute the Avro single-object-encoding CRC-64-AVRO fingerprint of the
/// parsing canonical form.
pub fn Schema::fingerprint64(self : Schema) -> UInt64 {
let bytes = @utf8.encode(self.canonical_form())
let empty = 0xc15d213aa4d7a795UL
let mut fingerprint = empty
for byte in bytes {
let mut value = (fingerprint ^ byte.to_uint64()) & 0xffUL
for _ in 0..<8 {
value = if (value & 1UL) == 1UL {
(value >> 1) ^ empty
} else {
value >> 1
}
}
fingerprint = (fingerprint >> 8) ^ value
}
fingerprint
}