1pub use serde_avro_fast;
7
8pub use serde_avro_derive_macros::*;
9
10use std::{any::TypeId, collections::HashMap};
11
12use serde_avro_fast::schema::*;
13
14pub trait BuildSchema {
19 fn schema() -> Result<Schema, SchemaError> {
21 Self::schema_mut().try_into()
22 }
23 fn schema_mut() -> SchemaMut {
25 let mut builder = SchemaBuilder::default();
26 Self::append_schema(&mut builder);
27 SchemaMut::from_nodes(builder.nodes)
28 }
29
30 fn append_schema(builder: &mut SchemaBuilder);
41
42 type TypeLookup: std::any::Any;
53}
54
55#[derive(Default)]
59#[non_exhaustive]
60pub struct SchemaBuilder {
61 pub nodes: Vec<SchemaNode>,
67 pub already_built_types: HashMap<TypeId, SchemaKey>,
78}
79
80impl SchemaBuilder {
81 pub fn reserve(&mut self) -> usize {
86 let idx = self.nodes.len();
87 self.nodes.push(RegularType::Null.into());
88 idx
89 }
90
91 pub fn find_or_build<T: BuildSchema + ?Sized>(&mut self) -> SchemaKey {
99 match self
100 .already_built_types
101 .entry(TypeId::of::<T::TypeLookup>())
102 {
103 std::collections::hash_map::Entry::Occupied(entry) => *entry.get(),
104 std::collections::hash_map::Entry::Vacant(entry) => {
105 let schema_key = SchemaKey::from_idx(self.nodes.len());
106 entry.insert(schema_key);
107 T::append_schema(self);
108 assert!(
109 self.nodes.len() > schema_key.idx(),
110 "append_schema should always insert at least a node \
111 (and its dependencies below itself)"
112 );
113 schema_key
114 }
115 }
116 }
117
118 pub fn build_duplicate<T: BuildSchema + ?Sized>(&mut self) -> SchemaKey {
127 let schema_key = SchemaKey::from_idx(self.nodes.len());
128 T::append_schema(self);
129 assert!(
130 self.nodes.len() > schema_key.idx(),
131 "append_schema should always insert at least a node \
132 (and its dependencies below itself)"
133 );
134 schema_key
135 }
136
137 pub fn build_logical_type(
144 &mut self,
145 logical_type: LogicalType,
146 inner_type_duplicate: impl FnOnce(&mut Self) -> SchemaKey,
147 name_override: impl FnOnce() -> String,
148 ) -> SchemaKey {
149 let inner_type_duplicate_key = inner_type_duplicate(self);
150 let node = &mut self.nodes[inner_type_duplicate_key.idx()];
151 node.logical_type = Some(logical_type);
152
153 if let Some(name) = node.type_.name_mut() {
154 *name = Name::from_fully_qualified_name(name_override());
155 }
156
157 inner_type_duplicate_key
158 }
159}
160
161macro_rules! impl_primitive {
162 ($($ty:ty, $variant:ident;)+) => {
163 $(
164 impl BuildSchema for $ty {
165 fn append_schema(builder: &mut SchemaBuilder) {
166 builder.nodes.push(RegularType::$variant.into());
167 }
168 type TypeLookup = Self;
169 }
170 )*
171 };
172}
173impl_primitive!(
174 (), Null;
175 bool, Boolean;
176 i32, Int;
177 i64, Long;
178 f32, Float;
179 f64, Double;
180 String, String;
181 Vec<u8>, Bytes;
182);
183
184macro_rules! impl_forward {
185 ($($ty:ty, $to:ty;)+) => {
186 $(
187 impl BuildSchema for $ty {
188 fn append_schema(builder: &mut SchemaBuilder) {
189 <$to as BuildSchema>::append_schema(builder)
190 }
191 type TypeLookup = <$to as BuildSchema>::TypeLookup;
192 }
193 )*
194 };
195}
196impl_forward! {
197 str, String;
198 [u8], Vec<u8>;
199 u16, i32;
200 u32, i64;
201 u64, i64;
202 i8, i32;
203 i16, i32;
204 usize, i64;
205}
206
207macro_rules! impl_ptr {
208 ($($($ty_path:ident)::+,)+) => {
209 $(
210 impl<T: BuildSchema + ?Sized> BuildSchema for $($ty_path)::+<T> {
211 fn append_schema(builder: &mut SchemaBuilder) {
212 <T as BuildSchema>::append_schema(builder)
213 }
214 type TypeLookup = T::TypeLookup;
215 }
216 )*
217 };
218}
219impl_ptr! {
220 Box,
221 std::sync::Arc,
222 std::rc::Rc,
223 std::cell::RefCell,
224 std::cell::Cell,
225}
226impl<T: BuildSchema + ?Sized> BuildSchema for &'_ T {
227 fn append_schema(builder: &mut SchemaBuilder) {
228 <T as BuildSchema>::append_schema(builder)
229 }
230 type TypeLookup = T::TypeLookup;
231}
232impl<T: BuildSchema + ?Sized> BuildSchema for &'_ mut T {
233 fn append_schema(builder: &mut SchemaBuilder) {
234 <T as BuildSchema>::append_schema(builder)
235 }
236 type TypeLookup = T::TypeLookup;
237}
238
239impl<T: BuildSchema> BuildSchema for Vec<T> {
240 fn append_schema(builder: &mut SchemaBuilder) {
241 let reserved_schema_key = builder.reserve();
242 let new_node = Array::new(builder.find_or_build::<T>()).into();
243 builder.nodes[reserved_schema_key] = new_node;
244 }
245
246 type TypeLookup = Vec<T::TypeLookup>;
247}
248
249impl<T: BuildSchema> BuildSchema for [T] {
250 fn append_schema(builder: &mut SchemaBuilder) {
251 <Vec<T> as BuildSchema>::append_schema(builder)
252 }
253 type TypeLookup = <Vec<T> as BuildSchema>::TypeLookup;
254}
255
256impl<T: BuildSchema> BuildSchema for Option<T> {
257 fn append_schema(builder: &mut SchemaBuilder) {
258 let reserved_schema_key = builder.reserve();
259 let new_node = Union::new(vec![
260 builder.find_or_build::<()>(),
261 builder.find_or_build::<T>(),
262 ])
263 .into();
264 builder.nodes[reserved_schema_key] = new_node;
265 }
266
267 type TypeLookup = Option<T::TypeLookup>;
268}
269
270impl<const N: usize> BuildSchema for [u8; N] {
271 fn append_schema(builder: &mut SchemaBuilder) {
272 builder.nodes.push(
273 Fixed::new(
274 Name::from_fully_qualified_name(format!("u8_array_{}", N)),
275 N,
276 )
277 .into(),
278 );
279 }
280 type TypeLookup = Self;
281}
282
283impl<S: std::ops::Deref<Target = str>, V: BuildSchema> BuildSchema for HashMap<S, V> {
284 fn append_schema(builder: &mut SchemaBuilder) {
285 let reserved_schema_key = builder.reserve();
286 let new_node = Map::new(builder.find_or_build::<V>()).into();
287 builder.nodes[reserved_schema_key] = new_node;
288 }
289 type TypeLookup = HashMap<String, V::TypeLookup>;
290}
291impl<S: std::ops::Deref<Target = str>, V: BuildSchema> BuildSchema
292 for std::collections::BTreeMap<S, V>
293{
294 fn append_schema(builder: &mut SchemaBuilder) {
295 <HashMap<String, V> as BuildSchema>::append_schema(builder)
296 }
297 type TypeLookup = <HashMap<String, V> as BuildSchema>::TypeLookup;
298}
299
300#[doc(hidden)]
301pub fn hash_type_id(struct_name: &mut String, type_id: TypeId) {
304 use std::{
305 fmt::Write,
306 hash::{Hash as _, Hasher as _},
307 };
308 #[allow(deprecated)] let mut hasher = std::hash::SipHasher::new();
310 type_id.hash(&mut hasher);
311 write!(struct_name, "_{:016x?}", hasher.finish()).unwrap();
312}
313
314#[doc(hidden)]
315pub enum LazyNamespace {
316 Pending(fn() -> String),
317 Computed(String),
318}
319impl LazyNamespace {
320 pub fn new(f: fn() -> String) -> Self {
321 Self::Pending(f)
322 }
323 pub fn get(&mut self) -> &str {
324 match self {
325 Self::Pending(f) => {
326 let n = f();
327 *self = Self::Computed(n);
328 match self {
329 Self::Computed(n) => n,
330 _ => unreachable!(),
331 }
332 }
333 Self::Computed(n) => n,
334 }
335 }
336}