This is an automated email from the ASF dual-hosted git repository. mgrigorov pushed a commit to branch 363-make-AvroSchemaComponent-always-available in repository https://gitbox.apache.org/repos/asf/avro-rs.git
commit f4636168f183115ce2c0d0cd6d6d931d42a6a0a4 Author: Martin Tzvetanov Grigorov <[email protected]> AuthorDate: Thu Jan 8 06:02:27 2026 +0200 feat!:Make AvroSchemaComponent available without `derive` feature Fixes #363 Signed-off-by: Martin Tzvetanov Grigorov <[email protected]> --- avro/src/lib.rs | 2 +- avro/src/schema.rs | 105 ++++++++++++++++++++++---------------------- avro_derive/src/lib.rs | 16 +++---- avro_derive/tests/derive.rs | 2 +- 4 files changed, 62 insertions(+), 63 deletions(-) diff --git a/avro/src/lib.rs b/avro/src/lib.rs index 5c744ff..bfeb6bf 100644 --- a/avro/src/lib.rs +++ b/avro/src/lib.rs @@ -984,7 +984,7 @@ 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 schema::{AvroSchema, AvroSchemaComponent, Schema}; pub use serde::{de::from_value, ser::to_value}; pub use uuid::Uuid; pub use writer::{ diff --git a/avro/src/schema.rs b/avro/src/schema.rs index 9d988c8..be728f3 100644 --- a/avro/src/schema.rs +++ b/avro/src/schema.rs @@ -2574,64 +2574,63 @@ pub trait AvroSchema { fn get_schema() -> Schema; } +/// Trait for types that serve as fully defined components inside an Avro data model. Derive +/// implementation available through `derive` feature. This is what is implemented by +/// the `derive(AvroSchema)` macro. +/// +/// # Implementation guide +/// +///### Simple implementation +/// To construct a non named simple schema, it is possible to ignore the input argument making the +/// general form implementation look like +/// ```ignore +/// impl AvroSchemaComponent for AType { +/// fn get_schema_in_ctxt(_: &mut Names, _: &Namespace) -> Schema { +/// Schema::? +/// } +///} +/// ``` +/// ### Passthrough implementation +/// To construct a schema for a Type that acts as in "inner" type, such as for smart pointers, simply +/// pass through the arguments to the inner type +/// ```ignore +/// impl AvroSchemaComponent for PassthroughType { +/// fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace: &Namespace) -> Schema { +/// InnerType::get_schema_in_ctxt(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; +} + #[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, diff --git a/avro_derive/src/lib.rs b/avro_derive/src/lib.rs index 8723593..c3587ed 100644 --- a/avro_derive/src/lib.rs +++ b/avro_derive/src/lib.rs @@ -84,7 +84,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::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; @@ -338,10 +338,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::AvroSchemaComponent>::get_schema_in_ctxt()` +/// - `A<T> -> <A<T> as apache_avro::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::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace)} } /// Stolen from serde @@ -496,7 +496,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::AvroSchemaComponent for Basic { fn get_schema_in_ctxt( named_schemas: &mut std::collections::HashMap< apache_avro::schema::Name, @@ -640,9 +640,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::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::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::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 72c6db0..cf3be7e 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}, + schema::{AvroSchema, AvroSchemaComponent}, }; use apache_avro_derive::*; use proptest::prelude::*;
