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 <[email protected]> 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::*;
