You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by kr...@apache.org on 2025/12/26 15:35:48 UTC
(avro-rs) branch feat/improve_documentation created (now c2af2c6)
This is an automated email from the ASF dual-hosted git repository.
kriskras99 pushed a change to branch feat/improve_documentation
in repository https://gitbox.apache.org/repos/asf/avro-rs.git
at c2af2c6 feat: Improve documentation for AvroSchema trait and derive
This branch includes the following new commits:
new c2af2c6 feat: Improve documentation for AvroSchema trait and derive
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(avro-rs) 01/01: feat: Improve documentation for AvroSchema trait and derive
Posted by kr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
kriskras99 pushed a commit to branch feat/improve_documentation
in repository https://gitbox.apache.org/repos/asf/avro-rs.git
commit c2af2c68cbd5cf6bb79e14115b1f30eb600f0645
Author: default <ad...@kriskras99.nl>
AuthorDate: Fri Dec 12 11:14:49 2025 +0000
feat: Improve documentation for AvroSchema trait and derive
---
.../test_interop_single_object_encoding.rs | 2 +-
avro/src/lib.rs | 25 +-
avro/src/reader.rs | 3 +-
avro/src/schema.rs | 212 +-------------
avro/src/{ => serde}/bytes.rs | 169 ++++++-----
avro/src/serde/de.rs | 4 +-
avro/src/serde/mod.rs | 314 ++++++++++++++++++++-
avro/src/serde/ser.rs | 36 +--
avro/src/types.rs | 21 +-
avro/src/writer.rs | 4 +-
avro/tests/avro-rs-285-bytes_deserialization.rs | 2 +-
avro_derive/src/lib.rs | 24 +-
avro_derive/tests/derive.rs | 2 +-
13 files changed, 463 insertions(+), 355 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 f75c5a3..4225812 100644
--- a/avro/src/lib.rs
+++ b/avro/src/lib.rs
@@ -868,13 +868,13 @@
//!
//! #[derive(Debug, Deserialize, Serialize)]
//! struct SampleStruct {
-//! #[serde(with = "apache_avro::serde_avro_bytes")]
+//! #[serde(with = "apache_avro::serde::bytes::avro_bytes")]
//! non_optional_bytes: Vec<u8>,
-//! #[serde(with = "apache_avro::serde_avro_bytes_opt")]
+//! #[serde(with = "apache_avro::serde::bytes::avro_bytes_opt")]
//! optional_bytes: Option<Vec<u8>>,
-//! #[serde(with = "apache_avro::serde_avro_fixed")]
+//! #[serde(with = "apache_avro::serde::bytes::avro_fixed")]
//! non_optional_fixed: [u8; 6],
-//! #[serde(with = "apache_avro::serde_avro_fixed_opt")]
+//! #[serde(with = "apache_avro::serde::bytes::avro_fixed_opt")]
//! optional_fixed: Option<[u8; 6]>,
//! }
//! ```
@@ -886,7 +886,7 @@
//!
//! #[derive(Debug, Deserialize, PartialEq, Serialize)]
//! struct ExampleByteArray {
-//! #[serde(with = "apache_avro::serde_avro_bytes_opt")]
+//! #[serde(with = "apache_avro::serde::bytes::avro_bytes_opt")]
//! data_bytes: Option<Vec<u8>>,
//! description: Option<String>,
//! }
@@ -943,14 +943,12 @@
//!
mod bigdecimal;
-mod bytes;
mod codec;
mod decimal;
mod decode;
mod duration;
mod encode;
mod reader;
-mod serde;
mod writer;
pub mod error;
@@ -959,17 +957,12 @@ pub mod rabin;
pub mod schema;
pub mod schema_compatibility;
pub mod schema_equality;
+pub mod serde;
pub mod types;
pub mod util;
pub mod validator;
-pub use crate::{
- bigdecimal::BigDecimal,
- bytes::{
- serde_avro_bytes, serde_avro_bytes_opt, serde_avro_fixed, serde_avro_fixed_opt,
- serde_avro_slice, serde_avro_slice_opt,
- },
-};
+pub use crate::bigdecimal::BigDecimal;
#[cfg(feature = "bzip")]
pub use codec::bzip::Bzip2Settings;
#[cfg(feature = "xz")]
@@ -984,8 +977,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, Schema};
-pub use serde::{de::from_value, ser::to_value};
+pub use schema::Schema;
+pub use serde::{AvroSchema, 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.rs b/avro/src/schema.rs
index a7ea493..8f5ce95 100644
--- a/avro/src/schema.rs
+++ b/avro/src/schema.rs
@@ -240,7 +240,7 @@ pub type Documentation = Option<String>;
/// Represents the aliases for Named Schema
pub type Aliases = Option<Vec<Alias>>;
/// Represents Schema lookup within a schema env
-pub(crate) type Names = HashMap<Name, Schema>;
+pub type Names = HashMap<Name, Schema>;
/// Represents Schema lookup within a schema
pub type NamesRef<'a> = HashMap<Name, &'a Schema>;
/// Represents the namespace for Named Schema
@@ -315,17 +315,19 @@ impl Name {
}
/// Return the fully qualified name needed for indexing or searching for the schema within a schema/schema env context. Puts the enclosing namespace into the name's namespace for clarity in schema/schema env parsing
- /// ```ignore
- /// use apache_avro::schema::Name;
- ///
+ /// ```
+ /// # use apache_avro::{Error, schema::Name};
+ /// # fn main() -> Result<(), Error> {
/// assert_eq!(
- /// Name::new("some_name")?.fully_qualified_name(&Some("some_namespace".into())),
- /// Name::new("some_namespace.some_name")?
+ /// Name::new("some_name")?.fully_qualified_name(&Some("some_namespace".into())),
+ /// Name::new("some_namespace.some_name")?
/// );
/// assert_eq!(
- /// Name::new("some_namespace.some_name")?.fully_qualified_name(&Some("other_namespace".into())),
- /// Name::new("some_namespace.some_name")?
+ /// Name::new("some_namespace.some_name")?.fully_qualified_name(&Some("other_namespace".into())),
+ /// Name::new("some_namespace.some_name")?
/// );
+ /// # Ok(())
+ /// # }
/// ```
pub fn fully_qualified_name(&self, enclosing_namespace: &Namespace) -> Name {
Name {
@@ -968,7 +970,7 @@ pub struct UnionSchema {
// schema index given a value.
// **NOTE** that this approach does not work for named types, and will have to be modified
// to support that. A simple solution is to also keep a mapping of the names used.
- variant_index: BTreeMap<SchemaKind, usize>,
+ pub(crate) variant_index: BTreeMap<SchemaKind, usize>,
}
impl UnionSchema {
@@ -2532,198 +2534,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 `apache_avro::schema::derive::AvroSchemaComponent` to get this trait
-/// through a blanket implementation.
-pub trait AvroSchema {
- fn get_schema() -> Schema;
-}
-
-#[cfg(feature = "derive")]
-pub mod derive {
- use super::*;
- use std::borrow::Cow;
-
- /// 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(names, 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,
- {
- 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!(uuid::Uuid, Schema::Uuid(UuidSchema::String));
- impl_schema!(core::time::Duration, Schema::Duration);
-
- 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 inner_schema = T::get_schema_in_ctxt(named_schemas, enclosing_namespace);
- Schema::Union(UnionSchema {
- schemas: vec![Schema::Null, inner_schema.clone()],
- variant_index: [Schema::Null, inner_schema]
- .iter()
- .enumerate()
- .map(|(idx, s)| (SchemaKind::from(s), idx))
- .collect(),
- })
- }
- }
-
- 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)
- }
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
diff --git a/avro/src/bytes.rs b/avro/src/serde/bytes.rs
similarity index 81%
rename from avro/src/bytes.rs
rename to avro/src/serde/bytes.rs
index 8ac98b5..e42cdf7 100644
--- a/avro/src/bytes.rs
+++ b/avro/src/serde/bytes.rs
@@ -18,14 +18,14 @@
use std::cell::Cell;
thread_local! {
- /// A thread local that is used to decide how to serialize Rust bytes into an Avro
- /// `types::Value` of type bytes.
+ /// A thread local that is used to decide if Rust bytes need to be serialized to
+ /// [`Value::Bytes`] or [`Value::Fixed`].
///
/// Relies on the fact that serde's serialization process is single-threaded.
pub(crate) static SER_BYTES_TYPE: Cell<BytesType> = const { Cell::new(BytesType::Bytes) };
- /// A thread local that is used to decide how to deserialize an Avro `types::Value`
- /// of type bytes into Rust bytes.
+ /// A thread local that is used to decide if a `Value::Bytes` needs to be deserialized to
+ /// a `Vec` or slice.
///
/// Relies on the fact that serde's deserialization process is single-threaded.
pub(crate) static DE_BYTES_BORROWED: Cell<bool> = const { Cell::new(false) };
@@ -40,23 +40,22 @@ pub(crate) enum BytesType {
/// Efficient (de)serialization of Avro bytes values.
///
/// This module is intended to be used through the Serde `with` attribute. Use
-/// [`serde_avro_bytes_opt`](crate::serde_avro_bytes_opt) for optional bytes.
+/// [`avro_bytes_opt`] for optional bytes.
///
-/// See usage with below example:
/// ```rust
-/// use apache_avro::{serde_avro_bytes, serde_avro_fixed};
+/// use apache_avro::serde::bytes::{avro_bytes, avro_fixed};
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Serialize, Deserialize)]
/// struct StructWithBytes {
-/// #[serde(with = "serde_avro_bytes")]
+/// #[serde(with = "avro_bytes")]
/// vec_field: Vec<u8>,
///
-/// #[serde(with = "serde_avro_fixed")]
+/// #[serde(with = "avro_fixed")]
/// fixed_field: [u8; 6],
/// }
/// ```
-pub mod serde_avro_bytes {
+pub mod avro_bytes {
use serde::{Deserializer, Serializer};
pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
@@ -77,23 +76,22 @@ pub mod serde_avro_bytes {
/// Efficient (de)serialization of optional Avro bytes values.
///
/// This module is intended to be used through the Serde `with` attribute. Use
-/// [`serde_avro_bytes`](crate::serde_avro_bytes) for non optional bytes.
+/// [`avro_bytes`] for non optional bytes.
///
-/// See usage with below example:
/// ```rust
-/// use apache_avro::{serde_avro_bytes_opt, serde_avro_fixed_opt};
+/// use apache_avro::serde::bytes::{avro_bytes_opt, avro_fixed_opt};
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Serialize, Deserialize)]
/// struct StructWithBytes {
-/// #[serde(with = "serde_avro_bytes_opt")]
+/// #[serde(with = "avro_bytes_opt")]
/// vec_field: Option<Vec<u8>>,
///
-/// #[serde(with = "serde_avro_fixed_opt")]
+/// #[serde(with = "avro_fixed_opt")]
/// fixed_field: Option<[u8; 6]>,
/// }
/// ```
-pub mod serde_avro_bytes_opt {
+pub mod avro_bytes_opt {
use serde::{Deserializer, Serializer};
use std::borrow::Borrow;
@@ -116,23 +114,22 @@ pub mod serde_avro_bytes_opt {
/// Efficient (de)serialization of Avro fixed values.
///
/// This module is intended to be used through the Serde `with` attribute. Use
-/// [`serde_avro_fixed_opt`](crate::serde_avro_fixed_opt) for optional fixed values.
+/// [`avro_fixed_opt`] for optional fixed values.
///
-/// See usage with below example:
/// ```rust
-/// use apache_avro::{serde_avro_bytes, serde_avro_fixed};
+/// use apache_avro::serde::bytes::{avro_bytes, avro_fixed};
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Serialize, Deserialize)]
/// struct StructWithBytes {
-/// #[serde(with = "serde_avro_bytes")]
+/// #[serde(with = "avro_bytes")]
/// vec_field: Vec<u8>,
///
-/// #[serde(with = "serde_avro_fixed")]
+/// #[serde(with = "avro_fixed")]
/// fixed_field: [u8; 6],
/// }
/// ```
-pub mod serde_avro_fixed {
+pub mod avro_fixed {
use super::{BytesType, SER_BYTES_TYPE};
use serde::{Deserializer, Serializer};
@@ -157,23 +154,22 @@ pub mod serde_avro_fixed {
/// Efficient (de)serialization of optional Avro fixed values.
///
/// This module is intended to be used through the Serde `with` attribute. Use
-/// [`serde_avro_fixed`](crate::serde_avro_fixed) for non optional fixed values.
+/// [`avro_fixed`] for non optional fixed values.
///
-/// See usage with below example:
/// ```rust
-/// use apache_avro::{serde_avro_bytes_opt, serde_avro_fixed_opt};
+/// use apache_avro::serde::bytes::{avro_bytes_opt, avro_fixed_opt};
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Serialize, Deserialize)]
/// struct StructWithBytes {
-/// #[serde(with = "serde_avro_bytes_opt")]
+/// #[serde(with = "avro_bytes_opt")]
/// vec_field: Option<Vec<u8>>,
///
-/// #[serde(with = "serde_avro_fixed_opt")]
+/// #[serde(with = "avro_fixed_opt")]
/// fixed_field: Option<[u8; 6]>,
/// }
/// ```
-pub mod serde_avro_fixed_opt {
+pub mod avro_fixed_opt {
use super::{BytesType, SER_BYTES_TYPE};
use serde::{Deserializer, Serializer};
use std::borrow::Borrow;
@@ -199,26 +195,27 @@ pub mod serde_avro_fixed_opt {
/// Efficient (de)serialization of Avro bytes/fixed borrowed values.
///
-/// This module is intended to be used through the Serde `with` attribute. Note that
-/// `bytes: &[u8]` are always serialized as
-/// [`Value::Bytes`](crate::types::Value::Bytes). However, both
-/// [`Value::Bytes`](crate::types::Value::Bytes) and
-/// [`Value::Fixed`](crate::types::Value::Fixed) can be deserialized as `bytes:
-/// &[u8]`. Use [`serde_avro_slice_opt`](crate::serde_avro_slice_opt) for optional
-/// bytes/fixed borrowed values.
+/// This module is intended to be used through the Serde `with` attribute.
+///
+/// Note that `&[u8]` are always serialized as [`Value::Bytes`]. However,
+/// both [`Value::Bytes`] and [`Value::Fixed`] can be deserialized as `&[u8]`.
+///
+/// Use [`avro_slice_opt`] for optional bytes/fixed borrowed values.
///
-/// See usage with below example:
/// ```rust
-/// use apache_avro::serde_avro_slice;
+/// use apache_avro::serde::bytes::avro_slice;
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Serialize, Deserialize)]
/// struct StructWithBytes<'a> {
-/// #[serde(with = "serde_avro_slice")]
+/// #[serde(with = "avro_slice")]
/// slice_field: &'a [u8],
/// }
/// ```
-pub mod serde_avro_slice {
+///
+/// [`Value::Bytes`]: crate::types::Value::Bytes
+/// [`Value::Fixed`]: crate::types::Value::Fixed
+pub mod avro_slice {
use super::DE_BYTES_BORROWED;
use serde::{Deserializer, Serializer};
@@ -242,26 +239,28 @@ pub mod serde_avro_slice {
/// Efficient (de)serialization of optional Avro bytes/fixed borrowed values.
///
-/// This module is intended to be used through the Serde `with` attribute. Note that
-/// `bytes: &[u8]` are always serialized as
-/// [`Value::Bytes`](crate::types::Value::Bytes). However, both
-/// [`Value::Bytes`](crate::types::Value::Bytes) and
-/// [`Value::Fixed`](crate::types::Value::Fixed) can be deserialized as `bytes:
-/// &[u8]`. Use [`serde_avro_slice`](crate::serde_avro_slice) for non optional
-/// bytes/fixed borrowed values.
+/// This module is intended to be used through the Serde `with` attribute.
+///
+/// Note that `&[u8]` are always serialized as [`Value::Bytes`]. However,
+/// both [`Value::Bytes`] and [`Value::Fixed`] can be deserialized as `&[u8]`.
+///
+/// Use [`avro_slice`] for non optional bytes/fixed borrowed values.
///
/// See usage with below example:
/// ```rust
-/// use apache_avro::serde_avro_slice_opt;
+/// use apache_avro::serde::bytes::avro_slice_opt;
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Serialize, Deserialize)]
/// struct StructWithBytes<'a> {
-/// #[serde(with = "serde_avro_slice_opt")]
+/// #[serde(with = "avro_slice_opt")]
/// slice_field: Option<&'a [u8]>,
/// }
/// ```
-pub mod serde_avro_slice_opt {
+///
+/// [`Value::Bytes`]: crate::types::Value::Bytes
+/// [`Value::Fixed`]: crate::types::Value::Fixed
+pub mod avro_slice_opt {
use super::DE_BYTES_BORROWED;
use serde::{Deserializer, Serializer};
use std::borrow::Borrow;
@@ -295,19 +294,19 @@ mod tests {
fn avro_3631_validate_schema_for_struct_with_byte_types() {
#[derive(Debug, Serialize)]
struct TestStructWithBytes<'a> {
- #[serde(with = "serde_avro_bytes")]
+ #[serde(with = "avro_bytes")]
vec_field: Vec<u8>,
- #[serde(with = "serde_avro_bytes_opt")]
+ #[serde(with = "avro_bytes_opt")]
vec_field_opt: Option<Vec<u8>>,
- #[serde(with = "serde_avro_fixed")]
+ #[serde(with = "avro_fixed")]
fixed_field: [u8; 6],
- #[serde(with = "serde_avro_fixed_opt")]
+ #[serde(with = "avro_fixed_opt")]
fixed_field_opt: Option<[u8; 7]>,
- #[serde(with = "serde_avro_slice")]
+ #[serde(with = "avro_slice")]
slice_field: &'a [u8],
- #[serde(with = "serde_avro_slice_opt")]
+ #[serde(with = "avro_slice_opt")]
slice_field_opt: Option<&'a [u8]>,
}
@@ -362,32 +361,32 @@ mod tests {
fn avro_3631_deserialize_value_to_struct_with_byte_types() {
#[derive(Debug, Deserialize, PartialEq)]
struct TestStructWithBytes<'a> {
- #[serde(with = "serde_avro_bytes")]
+ #[serde(with = "avro_bytes")]
vec_field: Vec<u8>,
- #[serde(with = "serde_avro_bytes_opt")]
+ #[serde(with = "avro_bytes_opt")]
vec_field_opt: Option<Vec<u8>>,
- #[serde(with = "serde_avro_bytes_opt")]
+ #[serde(with = "avro_bytes_opt")]
vec_field_opt2: Option<Vec<u8>>,
- #[serde(with = "serde_avro_fixed")]
+ #[serde(with = "avro_fixed")]
fixed_field: [u8; 6],
- #[serde(with = "serde_avro_fixed_opt")]
+ #[serde(with = "avro_fixed_opt")]
fixed_field_opt: Option<[u8; 7]>,
- #[serde(with = "serde_avro_fixed_opt")]
+ #[serde(with = "avro_fixed_opt")]
fixed_field_opt2: Option<[u8; 8]>,
- #[serde(with = "serde_avro_slice")]
+ #[serde(with = "avro_slice")]
slice_bytes_field: &'a [u8],
- #[serde(with = "serde_avro_slice_opt")]
+ #[serde(with = "avro_slice_opt")]
slice_bytes_field_opt: Option<&'a [u8]>,
- #[serde(with = "serde_avro_slice_opt")]
+ #[serde(with = "avro_slice_opt")]
slice_bytes_field_opt2: Option<&'a [u8]>,
- #[serde(with = "serde_avro_slice")]
+ #[serde(with = "avro_slice")]
slice_fixed_field: &'a [u8],
- #[serde(with = "serde_avro_slice_opt")]
+ #[serde(with = "avro_slice_opt")]
slice_fixed_field_opt: Option<&'a [u8]>,
- #[serde(with = "serde_avro_slice_opt")]
+ #[serde(with = "avro_slice_opt")]
slice_fixed_field_opt2: Option<&'a [u8]>,
}
@@ -491,46 +490,46 @@ mod tests {
array_field: &'a [u8],
vec_field: Vec<u8>,
- #[serde(with = "serde_avro_fixed")]
+ #[serde(with = "avro_fixed")]
vec_field2: Vec<u8>,
- #[serde(with = "serde_avro_fixed_opt")]
+ #[serde(with = "avro_fixed_opt")]
vec_field2_opt: Option<Vec<u8>>,
- #[serde(with = "serde_avro_fixed_opt")]
+ #[serde(with = "avro_fixed_opt")]
vec_field2_opt2: Option<Vec<u8>>,
- #[serde(with = "serde_avro_bytes")]
+ #[serde(with = "avro_bytes")]
vec_field3: Vec<u8>,
- #[serde(with = "serde_avro_bytes_opt")]
+ #[serde(with = "avro_bytes_opt")]
vec_field3_opt: Option<Vec<u8>>,
- #[serde(with = "serde_avro_bytes_opt")]
+ #[serde(with = "avro_bytes_opt")]
vec_field3_opt2: Option<Vec<u8>>,
- #[serde(with = "serde_avro_fixed")]
+ #[serde(with = "avro_fixed")]
fixed_field: [u8; 6],
- #[serde(with = "serde_avro_fixed_opt")]
+ #[serde(with = "avro_fixed_opt")]
fixed_field_opt: Option<[u8; 5]>,
- #[serde(with = "serde_avro_fixed_opt")]
+ #[serde(with = "avro_fixed_opt")]
fixed_field_opt2: Option<[u8; 4]>,
- #[serde(with = "serde_avro_fixed")]
+ #[serde(with = "avro_fixed")]
fixed_field2: &'a [u8],
- #[serde(with = "serde_avro_fixed_opt")]
+ #[serde(with = "avro_fixed_opt")]
fixed_field2_opt: Option<&'a [u8]>,
- #[serde(with = "serde_avro_fixed_opt")]
+ #[serde(with = "avro_fixed_opt")]
fixed_field2_opt2: Option<&'a [u8]>,
- #[serde(with = "serde_avro_bytes")]
+ #[serde(with = "avro_bytes")]
bytes_field: &'a [u8],
- #[serde(with = "serde_avro_bytes_opt")]
+ #[serde(with = "avro_bytes_opt")]
bytes_field_opt: Option<&'a [u8]>,
- #[serde(with = "serde_avro_bytes_opt")]
+ #[serde(with = "avro_bytes_opt")]
bytes_field_opt2: Option<&'a [u8]>,
- #[serde(with = "serde_avro_bytes")]
+ #[serde(with = "avro_bytes")]
bytes_field2: [u8; 6],
- #[serde(with = "serde_avro_bytes_opt")]
+ #[serde(with = "avro_bytes_opt")]
bytes_field2_opt: Option<[u8; 7]>,
- #[serde(with = "serde_avro_bytes_opt")]
+ #[serde(with = "avro_bytes_opt")]
bytes_field2_opt2: Option<[u8; 8]>,
}
diff --git a/avro/src/serde/de.rs b/avro/src/serde/de.rs
index c2007b5..b6b79db 100644
--- a/avro/src/serde/de.rs
+++ b/avro/src/serde/de.rs
@@ -16,7 +16,7 @@
// under the License.
//! Logic for serde-compatible deserialization.
-use crate::{Error, bytes::DE_BYTES_BORROWED, error::Details, types::Value};
+use crate::{Error, error::Details, serde::bytes::DE_BYTES_BORROWED, types::Value};
use serde::{
Deserialize,
de::{self, DeserializeSeed, Deserializer as _, Visitor},
@@ -758,7 +758,7 @@ impl<'de> de::Deserializer<'de> for StringDeserializer {
}
}
-/// Interpret a `Value` as an instance of type `D`.
+/// Deserialize from a [`Value`].
///
/// This conversion can fail if the structure of the `Value` does not match the
/// structure expected by `D`.
diff --git a/avro/src/serde/mod.rs b/avro/src/serde/mod.rs
index 509d2e5..3a11e12 100644
--- a/avro/src/serde/mod.rs
+++ b/avro/src/serde/mod.rs
@@ -1,4 +1,312 @@
-pub mod de;
-pub mod ser;
-pub mod ser_schema;
+//! Everything needed to use this crate with Serde.
+//!
+//! # Using `apache-avro` for `serde`
+//!
+//! Avro is a schema-based format, this means it requires a few extra steps to use compared to
+//! a data format like JSON.
+//!
+//! ## Schemas
+//! It's strongly recommended to derive the schemas for your types using the [`AvroSchema`] derive macro.
+//! The macro uses the Serde attributes to generate a matching schema and checks that no attributes are
+//! used that are incompatible with the Serde implementation in this crate. See the trait documenation for
+//! details on how to change the generated schema.
+//!
+//! Alternatively, you can write your own schema. If you go down this path, it is recommended you start with
+//! the schema derived by [`AvroSchema`] and then modify it to fit your needs.
+//!
+//! ### Using existing schemas
+//! If you have schemas that are already being used in other parts of your software stack, generating types
+//! from the schema can be very useful. There is a **third-party** crate [`rsgen-avro`] that implements this.
+//!
+//! ## Reading and writing data
+//!
+//! ```
+//! # use std::io::Cursor;
+//! # use serde::{Serialize, Deserialize};
+//! # use apache_avro::{AvroSchema, Error, Reader, Writer, serde::{from_value, to_value}};
+//! #
+//! # #[derive(PartialEq, Debug)]
+//! #[derive(Serialize, Deserialize, AvroSchema)]
+//! struct Foo {
+//! a: i64,
+//! b: String,
+//! }
+//!
+//! # fn main() -> Result<(), Error> {
+//! let schema = Foo::get_schema();
+//! // A writer needs the schema of the type that is going to be written
+//! let mut writer = Writer::new(&schema, Vec::new())?;
+//!
+//! let foo = Foo {
+//! a: 42,
+//! b: "Hello".to_string(),
+//! };
+//!
+//! // There are two ways to serialize data.
+//! // 1: Serialize directly to the writer:
+//! writer.append_ser(&foo)?;
+//! // 2: First serialize to an Avro `Value` then write that:
+//! let foo_value = to_value(&foo)?;
+//! writer.append(foo_value)?;
+//!
+//! // Always flush or consume the writer
+//! let data = writer.into_inner()?;
+//!
+//! // The reader does not need a schema as it's included in the data
+//! let reader = Reader::new(Cursor::new(data))?;
+//! // The reader is an iterator
+//! for result in reader {
+//! let value = result?;
+//! let new_foo: Foo = from_value(&value)?;
+//! assert_eq!(new_foo, foo);
+//! }
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! [`rsgen-avro`]: https://docs.rs/rsgen-avro/latest/rsgen_avro/
+
+use crate::{
+ Schema,
+ schema::{Names, Namespace, SchemaKind, UnionSchema},
+};
+
+mod de;
+mod ser;
+mod ser_schema;
mod util;
+
+pub mod bytes;
+
+pub use de::from_value;
+pub use ser::to_value;
+pub(crate) use ser_schema::SchemaAwareWriteSerializer;
+
+/// Trait for types that serve as an Avro data model.
+///
+/// Do not implement directly! Implement [`AvroSchemaComponent`] to get this trait through
+/// a blanket implementation or use the derive macro.
+///
+/// # Deriving `AvroSchema`
+///
+/// Using the custom derive requires that you enable the `"derive"` cargo
+/// feature in your `Cargo.toml`:
+///
+/// ```toml
+/// [dependencies]
+/// apache-avro = { version = "..", features = ["derive"] }
+/// ```
+///
+/// Then, you add the `#[derive(AvroSchema)]` annotation to your `struct` and
+/// `enum` type definition:
+///
+/// ```
+/// # use serde::{Serialize, Deserialize};
+/// # use apache_avro::AvroSchema;
+/// #[derive(AvroSchema, Serialize, Deserialize)]
+/// pub struct Foo {
+/// bar: Vec<Bar>,
+/// }
+///
+/// #[derive(AvroSchema, Serialize, Deserialize)]
+/// pub enum Bar {
+/// Spam,
+/// Maps
+/// }
+/// ```
+///
+/// This will implement [`AvroSchemaComponent`] for the type, and `AvroSchema`
+/// through the blanket implementation for `T: AvroSchemaComponent`.
+///
+/// Every member of the `struct` and `enum` must also implement `AvroSchemaComponent`.
+///
+/// # Compatibility with Serde attributes
+///
+/// The derive macro is compatible with most Serde attributes. It is incompatible with
+/// the following attributes:
+///
+/// - Container attributes
+/// - `tag`
+/// - `content`
+/// - `untagged`
+/// - `variant_identifier`
+/// - `field_identifier`
+/// - `remote`
+/// - `transparent`
+/// - `rename_all(serialize = "..", deserialize = "..")` where `serialize` != `deserialize`
+/// - Variant attributes
+/// - `other`
+/// - `untagged`
+/// - Field attributes
+/// - `getter`
+///
+/// The Serde attributes `skip_serializing` and `skip_serializing_if` require the `#[avro(default = "..")]`
+/// attribute because Avro does not support skipping fields.
+pub trait AvroSchema {
+ fn get_schema() -> Schema;
+}
+
+/// Trait for types that serve as fully defined components inside an Avro data model.
+///
+/// This can be derived via the [`AvroSchema`] derive.
+///
+/// # Implementation guide
+///
+/// ### Simple implementation
+/// To construct a unnamed simple schema, it is possible to ignore the input argument making the
+/// general form implementation look like
+/// ```
+/// # use apache_avro::{Schema, schema::{Names, Namespace}, serde::AvroSchemaComponent};
+/// # struct AType;
+/// impl AvroSchemaComponent for AType {
+/// fn get_schema_in_ctxt(_names: &mut Names, _enclosing_namespace: &Namespace) -> Schema {
+/// Schema::Int
+/// }
+/// }
+/// ```
+/// ### 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
+/// ```
+/// # use apache_avro::{Schema, schema::{Names, Namespace}, serde::AvroSchemaComponent};
+/// # struct InnerType;
+/// # impl AvroSchemaComponent for InnerType {
+/// # fn get_schema_in_ctxt(_names: &mut Names, _enclosing_namespace: &Namespace) -> Schema {
+/// # Schema::Int
+/// # }
+/// # }
+/// # struct PassthroughType(InnerType);
+/// impl AvroSchemaComponent for PassthroughType {
+/// fn get_schema_in_ctxt(names: &mut Names, enclosing_namespace: &Namespace) -> Schema {
+/// InnerType::get_schema_in_ctxt(names, 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
+/// # use apache_avro::{Schema, schema::{Name, Names, Namespace}, serde::AvroSchemaComponent};
+/// 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 = Name::new("MyName").unwrap().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,
+{
+ fn get_schema() -> Schema {
+ T::get_schema_in_ctxt(&mut std::collections::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!(uuid::Uuid, Schema::Uuid(crate::schema::UuidSchema::String));
+impl_schema!(core::time::Duration, Schema::Duration);
+
+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 inner_schema = T::get_schema_in_ctxt(named_schemas, enclosing_namespace);
+ Schema::Union(UnionSchema {
+ schemas: vec![Schema::Null, inner_schema.clone()],
+ variant_index: [Schema::Null, inner_schema]
+ .iter()
+ .enumerate()
+ .map(|(idx, s)| (SchemaKind::from(s), idx))
+ .collect(),
+ })
+ }
+}
+
+impl<T> AvroSchemaComponent for serde_json::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 std::collections::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 std::borrow::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)
+ }
+}
diff --git a/avro/src/serde/ser.rs b/avro/src/serde/ser.rs
index d78f501..8431cb1 100644
--- a/avro/src/serde/ser.rs
+++ b/avro/src/serde/ser.rs
@@ -18,14 +18,14 @@
//! Logic for serde-compatible serialization.
use crate::{
Error,
- bytes::{BytesType, SER_BYTES_TYPE},
+ serde::bytes::{BytesType, SER_BYTES_TYPE},
types::Value,
};
use serde::{Serialize, ser};
use std::{collections::HashMap, iter::once};
-#[derive(Clone, Default)]
-pub struct Serializer {}
+#[derive(Clone, Copy)]
+pub struct Serializer;
pub struct SeqSerializer {
items: Vec<Value>,
@@ -192,7 +192,7 @@ impl<'b> ser::Serializer for &'b mut Serializer {
where
T: Serialize + ?Sized,
{
- let v = value.serialize(&mut Serializer::default())?;
+ let v = value.serialize(&mut Serializer)?;
Ok(Value::from(Some(v)))
}
@@ -304,8 +304,7 @@ impl ser::SerializeSeq for SeqSerializer {
where
T: Serialize + ?Sized,
{
- self.items
- .push(value.serialize(&mut Serializer::default())?);
+ self.items.push(value.serialize(&mut Serializer)?);
Ok(())
}
@@ -356,7 +355,7 @@ impl ser::SerializeSeq for SeqVariantSerializer<'_> {
{
self.items.push(Value::Union(
self.index,
- Box::new(value.serialize(&mut Serializer::default())?),
+ Box::new(value.serialize(&mut Serializer)?),
));
Ok(())
}
@@ -396,7 +395,7 @@ impl ser::SerializeMap for MapSerializer {
where
T: Serialize + ?Sized,
{
- let key = key.serialize(&mut Serializer::default())?;
+ let key = key.serialize(&mut Serializer)?;
if let Value::String(key) = key {
self.indices.insert(key, self.values.len());
@@ -410,8 +409,7 @@ impl ser::SerializeMap for MapSerializer {
where
T: Serialize + ?Sized,
{
- self.values
- .push(value.serialize(&mut Serializer::default())?);
+ self.values.push(value.serialize(&mut Serializer)?);
Ok(())
}
@@ -435,10 +433,8 @@ impl ser::SerializeStruct for StructSerializer {
where
T: Serialize + ?Sized,
{
- self.fields.push((
- name.to_owned(),
- value.serialize(&mut Serializer::default())?,
- ));
+ self.fields
+ .push((name.to_owned(), value.serialize(&mut Serializer)?));
Ok(())
}
@@ -455,10 +451,8 @@ impl ser::SerializeStructVariant for StructVariantSerializer<'_> {
where
T: Serialize + ?Sized,
{
- self.fields.push((
- name.to_owned(),
- value.serialize(&mut Serializer::default())?,
- ));
+ self.fields
+ .push((name.to_owned(), value.serialize(&mut Serializer)?));
Ok(())
}
@@ -476,17 +470,17 @@ impl ser::SerializeStructVariant for StructVariantSerializer<'_> {
}
}
-/// Interpret a serializeable instance as a `Value`.
+/// Serialize to a [`Value`].
///
/// This conversion can fail if the value is not valid as per the Avro specification.
/// e.g: `HashMap` with non-string keys.
///
/// This function does not work if `S` has any fields (recursively) that have the `#[serde(flatten)]`
-/// attribute. Please use [`Writer::append_ser`] if that's the case.
+/// attribute. Please use [`Writer::append_ser`] instead.
///
/// [`Writer::append_ser`]: crate::Writer::append_ser
pub fn to_value<S: Serialize>(value: S) -> Result<Value, Error> {
- let mut serializer = Serializer::default();
+ let mut serializer = Serializer;
value.serialize(&mut serializer)
}
diff --git a/avro/src/types.rs b/avro/src/types.rs
index 8cca5b9..63e3df4 100644
--- a/avro/src/types.rs
+++ b/avro/src/types.rs
@@ -2749,7 +2749,7 @@ Field with name '"b"' is not a member of the map items"#,
#[test]
fn test_avro_3460_validation_with_refs_real_struct() -> TestResult {
- use crate::serde::ser::Serializer;
+ use crate::serde::to_value;
use serde::Serialize;
#[derive(Serialize, Clone)]
@@ -2814,12 +2814,9 @@ Field with name '"b"' is not a member of the map items"#,
b: None,
};
- let mut ser = Serializer::default();
- let test_outer1: Value = test_outer1.serialize(&mut ser)?;
- let mut ser = Serializer::default();
- let test_outer2: Value = test_outer2.serialize(&mut ser)?;
- let mut ser = Serializer::default();
- let test_outer3: Value = test_outer3.serialize(&mut ser)?;
+ let test_outer1 = to_value(test_outer1)?;
+ let test_outer2 = to_value(test_outer2)?;
+ let test_outer3 = to_value(test_outer3)?;
assert!(
!test_outer1.validate(&schema),
@@ -2838,7 +2835,7 @@ Field with name '"b"' is not a member of the map items"#,
}
fn avro_3674_with_or_without_namespace(with_namespace: bool) -> TestResult {
- use crate::serde::ser::Serializer;
+ use crate::serde::to_value;
use serde::Serialize;
let schema_str = r#"
@@ -2909,8 +2906,7 @@ Field with name '"b"' is not a member of the map items"#,
},
};
- let mut ser = Serializer::default();
- let test_value: Value = msg.serialize(&mut ser)?;
+ let test_value = to_value(msg)?;
assert!(test_value.validate(&schema), "test_value should validate");
assert!(
test_value.resolve(&schema).is_ok(),
@@ -2931,7 +2927,7 @@ Field with name '"b"' is not a member of the map items"#,
}
fn avro_3688_schema_resolution_panic(set_field_b: bool) -> TestResult {
- use crate::serde::ser::Serializer;
+ use crate::serde::to_value;
use serde::{Deserialize, Serialize};
let schema_str = r#"{
@@ -2992,8 +2988,7 @@ Field with name '"b"' is not a member of the map items"#,
},
};
- let mut ser = Serializer::default();
- let test_value: Value = msg.serialize(&mut ser)?;
+ let test_value = to_value(msg)?;
assert!(test_value.validate(&schema), "test_value should validate");
assert!(
test_value.resolve(&schema).is_ok(),
diff --git a/avro/src/writer.rs b/avro/src/writer.rs
index 7fda617..ea65213 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, SchemaAwareWriteSerializer},
types::Value,
};
use serde::Serialize;
diff --git a/avro/tests/avro-rs-285-bytes_deserialization.rs b/avro/tests/avro-rs-285-bytes_deserialization.rs
index 1262cea..d7190cd 100644
--- a/avro/tests/avro-rs-285-bytes_deserialization.rs
+++ b/avro/tests/avro-rs-285-bytes_deserialization.rs
@@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct ExampleByteArray {
- #[serde(with = "apache_avro::serde_avro_bytes_opt")]
+ #[serde(with = "apache_avro::serde::bytes::avro_bytes_opt")]
data_bytes: Option<Vec<u8>>,
description: Option<String>,
}
diff --git a/avro_derive/src/lib.rs b/avro_derive/src/lib.rs
index a331bdc..3e7d137 100644
--- a/avro_derive/src/lib.rs
+++ b/avro_derive/src/lib.rs
@@ -15,6 +15,14 @@
// specific language governing permissions and limitations
// under the License.
+//! This crate provides the `AvroSchema` derive macro.
+//! ```no_run
+//! #[derive(AvroSchema)]
+//! ```
+//! Please see the documentation of the [`AvroSchema`] trait for instructions on how to use it.
+//!
+//! [`AvroSchema`]: https://docs.rs/apache-avro/latest/apache_avro/schema/trait.AvroSchema.html
+
mod attributes;
mod case;
@@ -80,7 +88,7 @@ fn derive_avro_schema(input: &mut DeriveInput) -> Result<TokenStream, Vec<syn::E
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
Ok(quote! {
#[automatically_derived]
- impl #impl_generics apache_avro::schema::derive::AvroSchemaComponent for #ident #ty_generics #where_clause {
+ impl #impl_generics apache_avro::serde::AvroSchemaComponent for #ident #ty_generics #where_clause {
fn get_schema_in_ctxt(named_schemas: &mut std::collections::HashMap<apache_avro::schema::Name, apache_avro::schema::Schema>, enclosing_namespace: &Option<String>) -> apache_avro::schema::Schema {
let name = apache_avro::schema::Name::new(#full_schema_name).expect(&format!("Unable to parse schema name {}", #full_schema_name)[..]).fully_qualified_name(enclosing_namespace);
let enclosing_namespace = &name.namespace;
@@ -334,10 +342,10 @@ fn is_default_attr(attr: &Attribute) -> bool {
}
/// Generates the schema def expression for fully qualified type paths using the associated function
-/// - `A -> <A as apache_avro::schema::derive::AvroSchemaComponent>::get_schema_in_ctxt()`
-/// - `A<T> -> <A<T> as apache_avro::schema::derive::AvroSchemaComponent>::get_schema_in_ctxt()`
+/// - `A -> <A as apache_avro::serde::AvroSchemaComponent>::get_schema_in_ctxt()`
+/// - `A<T> -> <A<T> as apache_avro::serde::AvroSchemaComponent>::get_schema_in_ctxt()`
fn type_path_schema_expr(p: &TypePath) -> TokenStream {
- quote! {<#p as apache_avro::schema::derive::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace)}
+ quote! {<#p as apache_avro::serde::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace)}
}
/// Stolen from serde
@@ -498,7 +506,7 @@ mod tests {
assert!(derived.is_ok());
assert_eq!(derived.unwrap().to_string(), quote! {
#[automatically_derived]
- impl apache_avro::schema::derive::AvroSchemaComponent for Basic {
+ impl apache_avro::serde::AvroSchemaComponent for Basic {
fn get_schema_in_ctxt(
named_schemas: &mut std::collections::HashMap<
apache_avro::schema::Name,
@@ -642,9 +650,9 @@ mod tests {
#[test]
fn test_trait_cast() {
- assert_eq!(type_path_schema_expr(&syn::parse2::<TypePath>(quote!{i32}).unwrap()).to_string(), quote!{<i32 as apache_avro::schema::derive::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace)}.to_string());
- assert_eq!(type_path_schema_expr(&syn::parse2::<TypePath>(quote!{Vec<T>}).unwrap()).to_string(), quote!{<Vec<T> as apache_avro::schema::derive::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace)}.to_string());
- assert_eq!(type_path_schema_expr(&syn::parse2::<TypePath>(quote!{AnyType}).unwrap()).to_string(), quote!{<AnyType as apache_avro::schema::derive::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace)}.to_string());
+ assert_eq!(type_path_schema_expr(&syn::parse2::<TypePath>(quote!{i32}).unwrap()).to_string(), quote!{<i32 as apache_avro::serde::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace)}.to_string());
+ assert_eq!(type_path_schema_expr(&syn::parse2::<TypePath>(quote!{Vec<T>}).unwrap()).to_string(), quote!{<Vec<T> as apache_avro::serde::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace)}.to_string());
+ assert_eq!(type_path_schema_expr(&syn::parse2::<TypePath>(quote!{AnyType}).unwrap()).to_string(), quote!{<AnyType as apache_avro::serde::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace)}.to_string());
}
#[test]
diff --git a/avro_derive/tests/derive.rs b/avro_derive/tests/derive.rs
index a4614fb..4d5afcc 100644
--- a/avro_derive/tests/derive.rs
+++ b/avro_derive/tests/derive.rs
@@ -17,7 +17,7 @@
use apache_avro::{
Reader, Schema, Writer, from_value,
- schema::{AvroSchema, derive::AvroSchemaComponent},
+ serde::{AvroSchema, AvroSchemaComponent},
};
use apache_avro_derive::*;
use proptest::prelude::*;