This is an automated email from the ASF dual-hosted git repository.
kriskras99 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro-rs.git
The following commit(s) were added to refs/heads/main by this push:
new 1f168be chore: Move `AvroSchema` and `AvroSchemaComponent` to the
`serde` module (#433)
1f168be is described below
commit 1f168be4d30ef7f306d861c449bb3bef8397b27f
Author: Kriskras99 <[email protected]>
AuthorDate: Fri Jan 23 22:58:04 2026 +0100
chore: Move `AvroSchema` and `AvroSchemaComponent` to the `serde` module
(#433)
* chore: Move `AvroSchema` and `AvroSchemaComponent` to the `serde` module
* fix: Add missing license
* fix: Remove unnecessary import
---
.../test_interop_single_object_encoding.rs | 2 +-
avro/src/lib.rs | 4 +-
avro/src/reader.rs | 3 +-
avro/src/schema/mod.rs | 440 +------------------
avro/src/serde/derive.rs | 467 +++++++++++++++++++++
avro/src/serde/mod.rs | 2 +
avro/src/writer.rs | 4 +-
avro_derive/tests/derive.rs | 8 +-
8 files changed, 479 insertions(+), 451 deletions(-)
diff --git a/avro/examples/test_interop_single_object_encoding.rs
b/avro/examples/test_interop_single_object_encoding.rs
index 61b1e6f..e65b35e 100644
--- a/avro/examples/test_interop_single_object_encoding.rs
+++ b/avro/examples/test_interop_single_object_encoding.rs
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-use apache_avro::{schema::AvroSchema, types::Value};
+use apache_avro::{serde::AvroSchema, types::Value};
use std::error::Error;
struct InteropMessage;
diff --git a/avro/src/lib.rs b/avro/src/lib.rs
index 785d718..f60c8c2 100644
--- a/avro/src/lib.rs
+++ b/avro/src/lib.rs
@@ -982,8 +982,8 @@ pub use reader::{
GenericSingleObjectReader, Reader, SpecificSingleObjectReader,
from_avro_datum,
from_avro_datum_reader_schemata, from_avro_datum_schemata, read_marker,
};
-pub use schema::{AvroSchema, AvroSchemaComponent, Schema};
-pub use serde::{from_value, to_value};
+pub use schema::Schema;
+pub use serde::{AvroSchema, AvroSchemaComponent, from_value, to_value};
pub use uuid::Uuid;
pub use writer::{
GenericSingleObjectWriter, SpecificSingleObjectWriter, Writer,
WriterBuilder, to_avro_datum,
diff --git a/avro/src/reader.rs b/avro/src/reader.rs
index 23af5db..967edb5 100644
--- a/avro/src/reader.rs
+++ b/avro/src/reader.rs
@@ -23,9 +23,10 @@ use crate::{
from_value,
headers::{HeaderBuilder, RabinFingerprintHeader},
schema::{
- AvroSchema, Names, ResolvedOwnedSchema, ResolvedSchema, Schema,
resolve_names,
+ Names, ResolvedOwnedSchema, ResolvedSchema, Schema, resolve_names,
resolve_names_with_schemata,
},
+ serde::AvroSchema,
types::Value,
util,
};
diff --git a/avro/src/schema/mod.rs b/avro/src/schema/mod.rs
index f77854e..3e6e4b1 100644
--- a/avro/src/schema/mod.rs
+++ b/avro/src/schema/mod.rs
@@ -28,7 +28,7 @@ use serde::{
};
use serde_json::{Map, Value};
use std::{
- borrow::{Borrow, Cow},
+ borrow::Borrow,
collections::{BTreeMap, HashMap, HashSet},
fmt,
fmt::Debug,
@@ -1262,333 +1262,6 @@ fn field_ordering_position(field: &str) ->
Option<usize> {
.map(|pos| pos + 1)
}
-/// Trait for types that serve as an Avro data model. Derive implementation
available
-/// through `derive` feature. Do not implement directly!
-/// Implement [`AvroSchemaComponent`] to get this trait
-/// through a blanket implementation.
-pub trait AvroSchema {
- fn get_schema() -> Schema;
-}
-
-/// Trait for types that serve as fully defined components inside an Avro data
model. Derive
-/// implementation available through `derive` feature. This is what is
implemented by
-/// the `derive(AvroSchema)` macro.
-///
-/// # Implementation guide
-///
-/// ### Simple implementation
-/// To construct a non named simple schema, it is possible to ignore the input
argument making the
-/// general form implementation look like
-/// ```ignore
-/// impl AvroSchemaComponent for AType {
-/// fn get_schema_in_ctxt(_: &mut Names, _: &Namespace) -> Schema {
-/// Schema::?
-/// }
-///}
-/// ```
-///
-/// ### Passthrough implementation
-///
-/// To construct a schema for a Type that acts as in "inner" type, such as for
smart pointers, simply
-/// pass through the arguments to the inner type
-/// ```ignore
-/// impl AvroSchemaComponent for PassthroughType {
-/// fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
-/// InnerType::get_schema_in_ctxt(named_schemas, enclosing_namespace)
-/// }
-///}
-/// ```
-///
-/// ### Complex implementation
-///
-/// To implement this for Named schema there is a general form needed to avoid
creating invalid
-/// schemas or infinite loops.
-/// ```ignore
-/// impl AvroSchemaComponent for ComplexType {
-/// fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
-/// // Create the fully qualified name for your type given the
enclosing namespace
-/// let name = apache_avro::schema::Name::new("MyName")
-/// .expect("Unable to parse schema name")
-/// .fully_qualified_name(enclosing_namespace);
-/// let enclosing_namespace = &name.namespace;
-/// // Check, if your name is already defined, and if so, return a ref
to that name
-/// if named_schemas.contains_key(&name) {
-/// apache_avro::schema::Schema::Ref{name: name.clone()}
-/// } else {
-/// named_schemas.insert(name.clone(),
apache_avro::schema::Schema::Ref{name: name.clone()});
-/// // YOUR SCHEMA DEFINITION HERE with the name equivalent to
"MyName".
-/// // For non-simple sub types delegate to their implementation
of AvroSchemaComponent
-/// }
-/// }
-///}
-/// ```
-pub trait AvroSchemaComponent {
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema;
-}
-
-impl<T> AvroSchema for T
-where
- T: AvroSchemaComponent + ?Sized,
-{
- fn get_schema() -> Schema {
- T::get_schema_in_ctxt(&mut HashMap::default(), &None)
- }
-}
-
-macro_rules! impl_schema (
- ($type:ty, $variant_constructor:expr) => (
- impl AvroSchemaComponent for $type {
- fn get_schema_in_ctxt(_: &mut Names, _: &Namespace) -> Schema {
- $variant_constructor
- }
- }
- );
-);
-
-impl_schema!(bool, Schema::Boolean);
-impl_schema!(i8, Schema::Int);
-impl_schema!(i16, Schema::Int);
-impl_schema!(i32, Schema::Int);
-impl_schema!(i64, Schema::Long);
-impl_schema!(u8, Schema::Int);
-impl_schema!(u16, Schema::Int);
-impl_schema!(u32, Schema::Long);
-impl_schema!(f32, Schema::Float);
-impl_schema!(f64, Schema::Double);
-impl_schema!(String, Schema::String);
-impl_schema!(str, Schema::String);
-impl_schema!(char, Schema::String);
-
-impl<T> AvroSchemaComponent for &T
-where
- T: AvroSchemaComponent + ?Sized,
-{
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- T::get_schema_in_ctxt(named_schemas, enclosing_namespace)
- }
-}
-
-impl<T> AvroSchemaComponent for &mut T
-where
- T: AvroSchemaComponent + ?Sized,
-{
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- T::get_schema_in_ctxt(named_schemas, enclosing_namespace)
- }
-}
-
-impl<T> AvroSchemaComponent for [T]
-where
- T: AvroSchemaComponent,
-{
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- Schema::array(T::get_schema_in_ctxt(named_schemas,
enclosing_namespace))
- }
-}
-
-impl<const N: usize, T> AvroSchemaComponent for [T; N]
-where
- T: AvroSchemaComponent,
-{
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- Schema::array(T::get_schema_in_ctxt(named_schemas,
enclosing_namespace))
- }
-}
-
-impl<T> AvroSchemaComponent for Vec<T>
-where
- T: AvroSchemaComponent,
-{
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- Schema::array(T::get_schema_in_ctxt(named_schemas,
enclosing_namespace))
- }
-}
-
-impl<T> AvroSchemaComponent for Option<T>
-where
- T: AvroSchemaComponent,
-{
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- let variants = vec![
- Schema::Null,
- T::get_schema_in_ctxt(named_schemas, enclosing_namespace),
- ];
-
- Schema::Union(
- UnionSchema::new(variants).expect("Option<T> must produce a valid
(non-nested) union"),
- )
- }
-}
-
-impl<T> AvroSchemaComponent for Map<String, T>
-where
- T: AvroSchemaComponent,
-{
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- Schema::map(T::get_schema_in_ctxt(named_schemas, enclosing_namespace))
- }
-}
-
-impl<T> AvroSchemaComponent for HashMap<String, T>
-where
- T: AvroSchemaComponent,
-{
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- Schema::map(T::get_schema_in_ctxt(named_schemas, enclosing_namespace))
- }
-}
-
-impl<T> AvroSchemaComponent for Box<T>
-where
- T: AvroSchemaComponent,
-{
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- T::get_schema_in_ctxt(named_schemas, enclosing_namespace)
- }
-}
-
-impl<T> AvroSchemaComponent for std::sync::Mutex<T>
-where
- T: AvroSchemaComponent,
-{
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- T::get_schema_in_ctxt(named_schemas, enclosing_namespace)
- }
-}
-
-impl<T> AvroSchemaComponent for Cow<'_, T>
-where
- T: AvroSchemaComponent + Clone,
-{
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- T::get_schema_in_ctxt(named_schemas, enclosing_namespace)
- }
-}
-
-impl AvroSchemaComponent for core::time::Duration {
- /// The schema is [`Schema::Duration`] with the name `duration`.
- ///
- /// This is a lossy conversion as this Avro type does not store the amount
of nanoseconds.
- #[expect(clippy::map_entry, reason = "We don't use the value from the
map")]
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- let name = Name::new("duration")
- .expect("Name is valid")
- .fully_qualified_name(enclosing_namespace);
- if named_schemas.contains_key(&name) {
- Schema::Ref { name }
- } else {
- let schema = Schema::Duration(FixedSchema {
- name: name.clone(),
- aliases: None,
- doc: None,
- size: 12,
- default: None,
- attributes: Default::default(),
- });
- named_schemas.insert(name, schema.clone());
- schema
- }
- }
-}
-
-impl AvroSchemaComponent for uuid::Uuid {
- /// The schema is [`Schema::Uuid`] with the name `uuid`.
- ///
- /// The underlying schema is [`Schema::Fixed`] with a size of 16.
- #[expect(clippy::map_entry, reason = "We don't use the value from the
map")]
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- let name = Name::new("uuid")
- .expect("Name is valid")
- .fully_qualified_name(enclosing_namespace);
- if named_schemas.contains_key(&name) {
- Schema::Ref { name }
- } else {
- let schema = Schema::Uuid(UuidSchema::Fixed(FixedSchema {
- name: name.clone(),
- aliases: None,
- doc: None,
- size: 16,
- default: None,
- attributes: Default::default(),
- }));
- named_schemas.insert(name, schema.clone());
- schema
- }
- }
-}
-
-impl AvroSchemaComponent for u64 {
- /// The schema is [`Schema::Fixed`] of size 8 with the name `u64`.
- #[expect(clippy::map_entry, reason = "We don't use the value from the
map")]
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- let name = Name::new("u64")
- .expect("Name is valid")
- .fully_qualified_name(enclosing_namespace);
- if named_schemas.contains_key(&name) {
- Schema::Ref { name }
- } else {
- let schema = Schema::Fixed(FixedSchema {
- name: name.clone(),
- aliases: None,
- doc: None,
- size: 8,
- default: None,
- attributes: Default::default(),
- });
- named_schemas.insert(name, schema.clone());
- schema
- }
- }
-}
-
-impl AvroSchemaComponent for u128 {
- /// The schema is [`Schema::Fixed`] of size 16 with the name `u128`.
- #[expect(clippy::map_entry, reason = "We don't use the value from the
map")]
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- let name = Name::new("u128")
- .expect("Name is valid")
- .fully_qualified_name(enclosing_namespace);
- if named_schemas.contains_key(&name) {
- Schema::Ref { name }
- } else {
- let schema = Schema::Fixed(FixedSchema {
- name: name.clone(),
- aliases: None,
- doc: None,
- size: 16,
- default: None,
- attributes: Default::default(),
- });
- named_schemas.insert(name, schema.clone());
- schema
- }
- }
-}
-
-impl AvroSchemaComponent for i128 {
- /// The schema is [`Schema::Fixed`] of size 16 with the name `i128`.
- #[expect(clippy::map_entry, reason = "We don't use the value from the
map")]
- fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
- let name = Name::new("i128")
- .expect("Name is valid")
- .fully_qualified_name(enclosing_namespace);
- if named_schemas.contains_key(&name) {
- Schema::Ref { name }
- } else {
- let schema = Schema::Fixed(FixedSchema {
- name: name.clone(),
- aliases: None,
- doc: None,
- size: 16,
- default: None,
- attributes: Default::default(),
- });
- named_schemas.insert(name, schema.clone());
- schema
- }
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
@@ -6359,117 +6032,6 @@ mod tests {
Ok(())
}
- #[test]
- fn avro_rs_401_str() -> TestResult {
- let schema = str::get_schema();
- assert_eq!(schema, Schema::String);
-
- Ok(())
- }
-
- #[test]
- fn avro_rs_401_references() -> TestResult {
- let schema_ref = <&str>::get_schema();
- let schema_ref_mut = <&mut str>::get_schema();
-
- assert_eq!(schema_ref, Schema::String);
- assert_eq!(schema_ref_mut, Schema::String);
-
- Ok(())
- }
-
- #[test]
- fn avro_rs_401_slice() -> TestResult {
- let schema = <[u8]>::get_schema();
- assert_eq!(schema, Schema::array(Schema::Int));
-
- Ok(())
- }
-
- #[test]
- fn avro_rs_401_array() -> TestResult {
- let schema = <[u8; 55]>::get_schema();
- assert_eq!(schema, Schema::array(Schema::Int));
-
- Ok(())
- }
-
- #[test]
- fn avro_rs_401_option_ref_slice_array() -> TestResult {
- let schema = <Option<&[[u8; 55]]>>::get_schema();
- assert_eq!(
- schema,
- Schema::union(vec![
- Schema::Null,
- Schema::array(Schema::array(Schema::Int))
- ])?
- );
-
- Ok(())
- }
-
- #[test]
- fn avro_rs_414_char() -> TestResult {
- let schema = char::get_schema();
- assert_eq!(schema, Schema::String);
-
- Ok(())
- }
-
- #[test]
- fn avro_rs_414_u64() -> TestResult {
- let schema = u64::get_schema();
- assert_eq!(
- schema,
- Schema::Fixed(FixedSchema {
- name: Name::new("u64")?,
- aliases: None,
- doc: None,
- size: 8,
- default: None,
- attributes: Default::default(),
- })
- );
-
- Ok(())
- }
-
- #[test]
- fn avro_rs_414_i128() -> TestResult {
- let schema = i128::get_schema();
- assert_eq!(
- schema,
- Schema::Fixed(FixedSchema {
- name: Name::new("i128")?,
- aliases: None,
- doc: None,
- size: 16,
- default: None,
- attributes: Default::default(),
- })
- );
-
- Ok(())
- }
-
- #[test]
- fn avro_rs_414_u128() -> TestResult {
- let schema = u128::get_schema();
- assert_eq!(
- schema,
- Schema::Fixed(FixedSchema {
- name: Name::new("u128")?,
- aliases: None,
- doc: None,
- size: 16,
- default: None,
- attributes: Default::default(),
- })
- );
-
- Ok(())
- }
-
#[test]
fn avro_rs_420_independent_canonical_form() -> TestResult {
let (record, schemata) = Schema::parse_str_with_list(
diff --git a/avro/src/serde/derive.rs b/avro/src/serde/derive.rs
new file mode 100644
index 0000000..b51cee7
--- /dev/null
+++ b/avro/src/serde/derive.rs
@@ -0,0 +1,467 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use crate::Schema;
+use crate::schema::{FixedSchema, Name, Names, Namespace, UnionSchema,
UuidSchema};
+use serde_json::Map;
+use std::borrow::Cow;
+use std::collections::HashMap;
+
+/// Trait for types that serve as an Avro data model. Derive implementation
available
+/// through `derive` feature. Do not implement directly!
+/// Implement [`AvroSchemaComponent`] to get this trait
+/// through a blanket implementation.
+pub trait AvroSchema {
+ fn get_schema() -> Schema;
+}
+
+/// Trait for types that serve as fully defined components inside an Avro data
model. Derive
+/// implementation available through `derive` feature. This is what is
implemented by
+/// the `derive(AvroSchema)` macro.
+///
+/// # Implementation guide
+///
+/// ### Simple implementation
+/// To construct a non named simple schema, it is possible to ignore the input
argument making the
+/// general form implementation look like
+/// ```ignore
+/// impl AvroSchemaComponent for AType {
+/// fn get_schema_in_ctxt(_: &mut Names, _: &Namespace) -> Schema {
+/// Schema::?
+/// }
+///}
+/// ```
+///
+/// ### Passthrough implementation
+///
+/// To construct a schema for a Type that acts as in "inner" type, such as for
smart pointers, simply
+/// pass through the arguments to the inner type
+/// ```ignore
+/// impl AvroSchemaComponent for PassthroughType {
+/// fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+/// InnerType::get_schema_in_ctxt(named_schemas, enclosing_namespace)
+/// }
+///}
+/// ```
+///
+/// ### Complex implementation
+///
+/// To implement this for Named schema there is a general form needed to avoid
creating invalid
+/// schemas or infinite loops.
+/// ```ignore
+/// impl AvroSchemaComponent for ComplexType {
+/// fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+/// // Create the fully qualified name for your type given the
enclosing namespace
+/// let name = apache_avro::schema::Name::new("MyName")
+/// .expect("Unable to parse schema name")
+/// .fully_qualified_name(enclosing_namespace);
+/// let enclosing_namespace = &name.namespace;
+/// // Check, if your name is already defined, and if so, return a ref
to that name
+/// if named_schemas.contains_key(&name) {
+/// apache_avro::schema::Schema::Ref{name: name.clone()}
+/// } else {
+/// named_schemas.insert(name.clone(),
apache_avro::schema::Schema::Ref{name: name.clone()});
+/// // YOUR SCHEMA DEFINITION HERE with the name equivalent to
"MyName".
+/// // For non-simple sub types delegate to their implementation
of AvroSchemaComponent
+/// }
+/// }
+///}
+/// ```
+pub trait AvroSchemaComponent {
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema;
+}
+
+impl<T> AvroSchema for T
+where
+ T: AvroSchemaComponent + ?Sized,
+{
+ fn get_schema() -> Schema {
+ T::get_schema_in_ctxt(&mut HashMap::default(), &None)
+ }
+}
+
+macro_rules! impl_schema (
+ ($type:ty, $variant_constructor:expr) => (
+ impl AvroSchemaComponent for $type {
+ fn get_schema_in_ctxt(_: &mut Names, _: &Namespace) -> Schema {
+ $variant_constructor
+ }
+ }
+ );
+);
+
+impl_schema!(bool, Schema::Boolean);
+impl_schema!(i8, Schema::Int);
+impl_schema!(i16, Schema::Int);
+impl_schema!(i32, Schema::Int);
+impl_schema!(i64, Schema::Long);
+impl_schema!(u8, Schema::Int);
+impl_schema!(u16, Schema::Int);
+impl_schema!(u32, Schema::Long);
+impl_schema!(f32, Schema::Float);
+impl_schema!(f64, Schema::Double);
+impl_schema!(String, Schema::String);
+impl_schema!(str, Schema::String);
+impl_schema!(char, Schema::String);
+
+impl<T> AvroSchemaComponent for &T
+where
+ T: AvroSchemaComponent + ?Sized,
+{
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ T::get_schema_in_ctxt(named_schemas, enclosing_namespace)
+ }
+}
+
+impl<T> AvroSchemaComponent for &mut T
+where
+ T: AvroSchemaComponent + ?Sized,
+{
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ T::get_schema_in_ctxt(named_schemas, enclosing_namespace)
+ }
+}
+
+impl<T> AvroSchemaComponent for [T]
+where
+ T: AvroSchemaComponent,
+{
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ Schema::array(T::get_schema_in_ctxt(named_schemas,
enclosing_namespace))
+ }
+}
+
+impl<const N: usize, T> AvroSchemaComponent for [T; N]
+where
+ T: AvroSchemaComponent,
+{
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ Schema::array(T::get_schema_in_ctxt(named_schemas,
enclosing_namespace))
+ }
+}
+
+impl<T> AvroSchemaComponent for Vec<T>
+where
+ T: AvroSchemaComponent,
+{
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ Schema::array(T::get_schema_in_ctxt(named_schemas,
enclosing_namespace))
+ }
+}
+
+impl<T> AvroSchemaComponent for Option<T>
+where
+ T: AvroSchemaComponent,
+{
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ let variants = vec![
+ Schema::Null,
+ T::get_schema_in_ctxt(named_schemas, enclosing_namespace),
+ ];
+
+ Schema::Union(
+ UnionSchema::new(variants).expect("Option<T> must produce a valid
(non-nested) union"),
+ )
+ }
+}
+
+impl<T> AvroSchemaComponent for Map<String, T>
+where
+ T: AvroSchemaComponent,
+{
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ Schema::map(T::get_schema_in_ctxt(named_schemas, enclosing_namespace))
+ }
+}
+
+impl<T> AvroSchemaComponent for HashMap<String, T>
+where
+ T: AvroSchemaComponent,
+{
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ Schema::map(T::get_schema_in_ctxt(named_schemas, enclosing_namespace))
+ }
+}
+
+impl<T> AvroSchemaComponent for Box<T>
+where
+ T: AvroSchemaComponent,
+{
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ T::get_schema_in_ctxt(named_schemas, enclosing_namespace)
+ }
+}
+
+impl<T> AvroSchemaComponent for std::sync::Mutex<T>
+where
+ T: AvroSchemaComponent,
+{
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ T::get_schema_in_ctxt(named_schemas, enclosing_namespace)
+ }
+}
+
+impl<T> AvroSchemaComponent for Cow<'_, T>
+where
+ T: AvroSchemaComponent + Clone,
+{
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ T::get_schema_in_ctxt(named_schemas, enclosing_namespace)
+ }
+}
+
+impl AvroSchemaComponent for core::time::Duration {
+ /// The schema is [`Schema::Duration`] with the name `duration`.
+ ///
+ /// This is a lossy conversion as this Avro type does not store the amount
of nanoseconds.
+ #[expect(clippy::map_entry, reason = "We don't use the value from the
map")]
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ let name = Name::new("duration")
+ .expect("Name is valid")
+ .fully_qualified_name(enclosing_namespace);
+ if named_schemas.contains_key(&name) {
+ Schema::Ref { name }
+ } else {
+ let schema = Schema::Duration(FixedSchema {
+ name: name.clone(),
+ aliases: None,
+ doc: None,
+ size: 12,
+ default: None,
+ attributes: Default::default(),
+ });
+ named_schemas.insert(name, schema.clone());
+ schema
+ }
+ }
+}
+
+impl AvroSchemaComponent for uuid::Uuid {
+ /// The schema is [`Schema::Uuid`] with the name `uuid`.
+ ///
+ /// The underlying schema is [`Schema::Fixed`] with a size of 16.
+ #[expect(clippy::map_entry, reason = "We don't use the value from the
map")]
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ let name = Name::new("uuid")
+ .expect("Name is valid")
+ .fully_qualified_name(enclosing_namespace);
+ if named_schemas.contains_key(&name) {
+ Schema::Ref { name }
+ } else {
+ let schema = Schema::Uuid(UuidSchema::Fixed(FixedSchema {
+ name: name.clone(),
+ aliases: None,
+ doc: None,
+ size: 16,
+ default: None,
+ attributes: Default::default(),
+ }));
+ named_schemas.insert(name, schema.clone());
+ schema
+ }
+ }
+}
+
+impl AvroSchemaComponent for u64 {
+ /// The schema is [`Schema::Fixed`] of size 8 with the name `u64`.
+ #[expect(clippy::map_entry, reason = "We don't use the value from the
map")]
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ let name = Name::new("u64")
+ .expect("Name is valid")
+ .fully_qualified_name(enclosing_namespace);
+ if named_schemas.contains_key(&name) {
+ Schema::Ref { name }
+ } else {
+ let schema = Schema::Fixed(FixedSchema {
+ name: name.clone(),
+ aliases: None,
+ doc: None,
+ size: 8,
+ default: None,
+ attributes: Default::default(),
+ });
+ named_schemas.insert(name, schema.clone());
+ schema
+ }
+ }
+}
+
+impl AvroSchemaComponent for u128 {
+ /// The schema is [`Schema::Fixed`] of size 16 with the name `u128`.
+ #[expect(clippy::map_entry, reason = "We don't use the value from the
map")]
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ let name = Name::new("u128")
+ .expect("Name is valid")
+ .fully_qualified_name(enclosing_namespace);
+ if named_schemas.contains_key(&name) {
+ Schema::Ref { name }
+ } else {
+ let schema = Schema::Fixed(FixedSchema {
+ name: name.clone(),
+ aliases: None,
+ doc: None,
+ size: 16,
+ default: None,
+ attributes: Default::default(),
+ });
+ named_schemas.insert(name, schema.clone());
+ schema
+ }
+ }
+}
+
+impl AvroSchemaComponent for i128 {
+ /// The schema is [`Schema::Fixed`] of size 16 with the name `i128`.
+ #[expect(clippy::map_entry, reason = "We don't use the value from the
map")]
+ fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace:
&Namespace) -> Schema {
+ let name = Name::new("i128")
+ .expect("Name is valid")
+ .fully_qualified_name(enclosing_namespace);
+ if named_schemas.contains_key(&name) {
+ Schema::Ref { name }
+ } else {
+ let schema = Schema::Fixed(FixedSchema {
+ name: name.clone(),
+ aliases: None,
+ doc: None,
+ size: 16,
+ default: None,
+ attributes: Default::default(),
+ });
+ named_schemas.insert(name, schema.clone());
+ schema
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::schema::{FixedSchema, Name};
+ use crate::{AvroSchema, Schema};
+ use apache_avro_test_helper::TestResult;
+
+ #[test]
+ fn avro_rs_401_str() -> TestResult {
+ let schema = str::get_schema();
+ assert_eq!(schema, Schema::String);
+
+ Ok(())
+ }
+
+ #[test]
+ fn avro_rs_401_references() -> TestResult {
+ let schema_ref = <&str>::get_schema();
+ let schema_ref_mut = <&mut str>::get_schema();
+
+ assert_eq!(schema_ref, Schema::String);
+ assert_eq!(schema_ref_mut, Schema::String);
+
+ Ok(())
+ }
+
+ #[test]
+ fn avro_rs_401_slice() -> TestResult {
+ let schema = <[u8]>::get_schema();
+ assert_eq!(schema, Schema::array(Schema::Int));
+
+ Ok(())
+ }
+
+ #[test]
+ fn avro_rs_401_array() -> TestResult {
+ let schema = <[u8; 55]>::get_schema();
+ assert_eq!(schema, Schema::array(Schema::Int));
+
+ Ok(())
+ }
+
+ #[test]
+ fn avro_rs_401_option_ref_slice_array() -> TestResult {
+ let schema = <Option<&[[u8; 55]]>>::get_schema();
+ assert_eq!(
+ schema,
+ Schema::union(vec![
+ Schema::Null,
+ Schema::array(Schema::array(Schema::Int))
+ ])?
+ );
+
+ Ok(())
+ }
+
+ #[test]
+ fn avro_rs_414_char() -> TestResult {
+ let schema = char::get_schema();
+ assert_eq!(schema, Schema::String);
+
+ Ok(())
+ }
+
+ #[test]
+ fn avro_rs_414_u64() -> TestResult {
+ let schema = u64::get_schema();
+ assert_eq!(
+ schema,
+ Schema::Fixed(FixedSchema {
+ name: Name::new("u64")?,
+ aliases: None,
+ doc: None,
+ size: 8,
+ default: None,
+ attributes: Default::default(),
+ })
+ );
+
+ Ok(())
+ }
+
+ #[test]
+ fn avro_rs_414_i128() -> TestResult {
+ let schema = i128::get_schema();
+ assert_eq!(
+ schema,
+ Schema::Fixed(FixedSchema {
+ name: Name::new("i128")?,
+ aliases: None,
+ doc: None,
+ size: 16,
+ default: None,
+ attributes: Default::default(),
+ })
+ );
+
+ Ok(())
+ }
+
+ #[test]
+ fn avro_rs_414_u128() -> TestResult {
+ let schema = u128::get_schema();
+ assert_eq!(
+ schema,
+ Schema::Fixed(FixedSchema {
+ name: Name::new("u128")?,
+ aliases: None,
+ doc: None,
+ size: 16,
+ default: None,
+ attributes: Default::default(),
+ })
+ );
+
+ Ok(())
+ }
+}
diff --git a/avro/src/serde/mod.rs b/avro/src/serde/mod.rs
index 01852a8..9c1dea4 100644
--- a/avro/src/serde/mod.rs
+++ b/avro/src/serde/mod.rs
@@ -16,11 +16,13 @@
// under the License.
mod de;
+mod derive;
mod ser;
pub(crate) mod ser_schema;
mod util;
mod with;
pub use de::from_value;
+pub use derive::{AvroSchema, AvroSchemaComponent};
pub use ser::to_value;
pub use with::{bytes, bytes_opt, fixed, fixed_opt, slice, slice_opt};
diff --git a/avro/src/writer.rs b/avro/src/writer.rs
index 5573e25..4ec219f 100644
--- a/avro/src/writer.rs
+++ b/avro/src/writer.rs
@@ -21,8 +21,8 @@ use crate::{
encode::{encode, encode_internal, encode_to_vec},
error::Details,
headers::{HeaderBuilder, RabinFingerprintHeader},
- schema::{AvroSchema, Name, ResolvedOwnedSchema, ResolvedSchema, Schema},
- serde::ser_schema::SchemaAwareWriteSerializer,
+ schema::{Name, ResolvedOwnedSchema, ResolvedSchema, Schema},
+ serde::{AvroSchema, ser_schema::SchemaAwareWriteSerializer},
types::Value,
};
use serde::Serialize;
diff --git a/avro_derive/tests/derive.rs b/avro_derive/tests/derive.rs
index a9012ed..4ee27f0 100644
--- a/avro_derive/tests/derive.rs
+++ b/avro_derive/tests/derive.rs
@@ -16,13 +16,9 @@
// under the License.
use apache_avro::{
- Reader, Schema, Writer, from_value,
- schema::{
- Alias, AvroSchema, AvroSchemaComponent, EnumSchema, FixedSchema, Name,
Names, Namespace,
- RecordSchema,
- },
+ AvroSchema, AvroSchemaComponent, Reader, Schema, Writer, from_value,
+ schema::{Alias, EnumSchema, FixedSchema, Name, Names, Namespace,
RecordSchema},
};
-use apache_avro_derive::*;
use proptest::prelude::*;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use std::{borrow::Cow, collections::HashMap, sync::Mutex};