pub use serde_avro_fast;
pub use serde_avro_derive_macros::*;
use std::{any::TypeId, collections::HashMap};
use serde_avro_fast::schema::*;
pub trait BuildSchema {
fn schema() -> Result<Schema, SchemaError> {
Self::schema_mut().try_into()
}
fn schema_mut() -> SchemaMut {
let mut builder = SchemaBuilder::default();
Self::append_schema(&mut builder);
SchemaMut::from_nodes(builder.nodes)
}
fn append_schema(builder: &mut SchemaBuilder);
type TypeLookup: std::any::Any;
}
#[derive(Default)]
#[non_exhaustive]
pub struct SchemaBuilder {
pub nodes: Vec<SchemaNode>,
pub already_built_types: HashMap<TypeId, SchemaKey>,
}
impl SchemaBuilder {
pub fn reserve(&mut self) -> usize {
let idx = self.nodes.len();
self.nodes.push(RegularType::Null.into());
idx
}
pub fn find_or_build<T: BuildSchema + ?Sized>(&mut self) -> SchemaKey {
match self
.already_built_types
.entry(TypeId::of::<T::TypeLookup>())
{
std::collections::hash_map::Entry::Occupied(entry) => *entry.get(),
std::collections::hash_map::Entry::Vacant(entry) => {
let schema_key = SchemaKey::from_idx(self.nodes.len());
entry.insert(schema_key);
T::append_schema(self);
assert!(
self.nodes.len() > schema_key.idx(),
"append_schema should always insert at least a node \
(and its dependencies below itself)"
);
schema_key
}
}
}
pub fn build_duplicate<T: BuildSchema + ?Sized>(&mut self) -> SchemaKey {
let schema_key = SchemaKey::from_idx(self.nodes.len());
T::append_schema(self);
assert!(
self.nodes.len() > schema_key.idx(),
"append_schema should always insert at least a node \
(and its dependencies below itself)"
);
schema_key
}
pub fn build_logical_type(
&mut self,
logical_type: LogicalType,
inner_type_duplicate: impl FnOnce(&mut Self) -> SchemaKey,
name_override: impl FnOnce() -> String,
) -> SchemaKey {
let inner_type_duplicate_key = inner_type_duplicate(self);
let node = &mut self.nodes[inner_type_duplicate_key.idx()];
node.logical_type = Some(logical_type);
if let Some(name) = node.type_.name_mut() {
*name = Name::from_fully_qualified_name(name_override());
}
inner_type_duplicate_key
}
}
macro_rules! impl_primitive {
($($ty:ty, $variant:ident;)+) => {
$(
impl BuildSchema for $ty {
fn append_schema(builder: &mut SchemaBuilder) {
builder.nodes.push(RegularType::$variant.into());
}
type TypeLookup = Self;
}
)*
};
}
impl_primitive!(
(), Null;
bool, Boolean;
i32, Int;
i64, Long;
f32, Float;
f64, Double;
String, String;
Vec<u8>, Bytes;
);
macro_rules! impl_forward {
($($ty:ty, $to:ty;)+) => {
$(
impl BuildSchema for $ty {
fn append_schema(builder: &mut SchemaBuilder) {
<$to as BuildSchema>::append_schema(builder)
}
type TypeLookup = <$to as BuildSchema>::TypeLookup;
}
)*
};
}
impl_forward! {
str, String;
[u8], Vec<u8>;
u16, i32;
u32, i64;
u64, i64;
i8, i32;
i16, i32;
usize, i64;
}
macro_rules! impl_ptr {
($($($ty_path:ident)::+,)+) => {
$(
impl<T: BuildSchema + ?Sized> BuildSchema for $($ty_path)::+<T> {
fn append_schema(builder: &mut SchemaBuilder) {
<T as BuildSchema>::append_schema(builder)
}
type TypeLookup = T::TypeLookup;
}
)*
};
}
impl_ptr! {
Box,
std::sync::Arc,
std::rc::Rc,
std::cell::RefCell,
std::cell::Cell,
}
impl<T: BuildSchema + ?Sized> BuildSchema for &'_ T {
fn append_schema(builder: &mut SchemaBuilder) {
<T as BuildSchema>::append_schema(builder)
}
type TypeLookup = T::TypeLookup;
}
impl<T: BuildSchema + ?Sized> BuildSchema for &'_ mut T {
fn append_schema(builder: &mut SchemaBuilder) {
<T as BuildSchema>::append_schema(builder)
}
type TypeLookup = T::TypeLookup;
}
impl<T: BuildSchema> BuildSchema for Vec<T> {
fn append_schema(builder: &mut SchemaBuilder) {
let reserved_schema_key = builder.reserve();
let new_node = Array::new(builder.find_or_build::<T>()).into();
builder.nodes[reserved_schema_key] = new_node;
}
type TypeLookup = Vec<T::TypeLookup>;
}
impl<T: BuildSchema> BuildSchema for [T] {
fn append_schema(builder: &mut SchemaBuilder) {
<Vec<T> as BuildSchema>::append_schema(builder)
}
type TypeLookup = <Vec<T> as BuildSchema>::TypeLookup;
}
impl<T: BuildSchema> BuildSchema for Option<T> {
fn append_schema(builder: &mut SchemaBuilder) {
let reserved_schema_key = builder.reserve();
let new_node = Union::new(vec![
builder.find_or_build::<()>(),
builder.find_or_build::<T>(),
])
.into();
builder.nodes[reserved_schema_key] = new_node;
}
type TypeLookup = Option<T::TypeLookup>;
}
impl<const N: usize> BuildSchema for [u8; N] {
fn append_schema(builder: &mut SchemaBuilder) {
builder.nodes.push(
Fixed::new(
Name::from_fully_qualified_name(format!("u8_array_{}", N)),
N,
)
.into(),
);
}
type TypeLookup = Self;
}
impl<S: std::ops::Deref<Target = str>, V: BuildSchema> BuildSchema for HashMap<S, V> {
fn append_schema(builder: &mut SchemaBuilder) {
let reserved_schema_key = builder.reserve();
let new_node = Map::new(builder.find_or_build::<V>()).into();
builder.nodes[reserved_schema_key] = new_node;
}
type TypeLookup = HashMap<String, V::TypeLookup>;
}
impl<S: std::ops::Deref<Target = str>, V: BuildSchema> BuildSchema
for std::collections::BTreeMap<S, V>
{
fn append_schema(builder: &mut SchemaBuilder) {
<HashMap<String, V> as BuildSchema>::append_schema(builder)
}
type TypeLookup = <HashMap<String, V> as BuildSchema>::TypeLookup;
}
#[doc(hidden)]
pub fn hash_type_id(struct_name: &mut String, type_id: TypeId) {
use std::{
fmt::Write,
hash::{Hash as _, Hasher as _},
};
#[allow(deprecated)] let mut hasher = std::hash::SipHasher::new();
type_id.hash(&mut hasher);
write!(struct_name, "_{:016x?}", hasher.finish()).unwrap();
}
#[doc(hidden)]
pub enum LazyNamespace {
Pending(fn() -> String),
Computed(String),
}
impl LazyNamespace {
pub fn new(f: fn() -> String) -> Self {
Self::Pending(f)
}
pub fn get(&mut self) -> &str {
match self {
Self::Pending(f) => {
let n = f();
*self = Self::Computed(n);
match self {
Self::Computed(n) => n,
_ => unreachable!(),
}
}
Self::Computed(n) => n,
}
}
}