This is an automated email from the ASF dual-hosted git repository. kriskras99 pushed a commit to branch chore/avro_derive_enum_module in repository https://gitbox.apache.org/repos/asf/avro-rs.git
commit 1a3ad2c8d06c3726af93d7a897db124246cbf8d6 Author: Kriskras99 <[email protected]> AuthorDate: Wed Feb 25 22:11:41 2026 +0100 chore: Move enum derive to `enums::plain` in `avro_derive` Prep for more complete enum support --- avro_derive/src/enums/mod.rs | 50 +++++++++++++++++++++++++++ avro_derive/src/enums/plain.rs | 49 +++++++++++++++++++++++++++ avro_derive/src/lib.rs | 77 +++--------------------------------------- 3 files changed, 103 insertions(+), 73 deletions(-) diff --git a/avro_derive/src/enums/mod.rs b/avro_derive/src/enums/mod.rs new file mode 100644 index 0000000..9613124 --- /dev/null +++ b/avro_derive/src/enums/mod.rs @@ -0,0 +1,50 @@ +mod plain; + +use crate::attributes::NamedTypeOptions; +use proc_macro2::{Ident, Span, TokenStream}; +use syn::{Attribute, DataEnum, Fields, Meta}; + +/// Generate a schema definition for a enum. +pub fn get_data_enum_schema_def( + container_attrs: &NamedTypeOptions, + data_enum: DataEnum, + ident_span: Span, +) -> Result<TokenStream, Vec<syn::Error>> { + if data_enum.variants.iter().all(|v| Fields::Unit == v.fields) { + plain::schema_def(container_attrs, data_enum, ident_span) + } else { + Err(vec![syn::Error::new( + ident_span, + "AvroSchema: derive does not work for enums with non unit structs", + )]) + } +} + +fn default_enum_variant( + data_enum: &DataEnum, + error_span: Span, +) -> Result<Option<String>, Vec<syn::Error>> { + match data_enum + .variants + .iter() + .filter(|v| v.attrs.iter().any(is_default_attr)) + .collect::<Vec<_>>() + { + variants if variants.is_empty() => Ok(None), + single if single.len() == 1 => Ok(Some(single[0].ident.to_string())), + multiple => Err(vec![syn::Error::new( + error_span, + format!( + "Multiple defaults defined: {:?}", + multiple + .iter() + .map(|v| v.ident.to_string()) + .collect::<Vec<String>>() + ), + )]), + } +} + +fn is_default_attr(attr: &Attribute) -> bool { + matches!(attr, Attribute { meta: Meta::Path(path), .. } if path.get_ident().map(Ident::to_string).as_deref() == Some("default")) +} diff --git a/avro_derive/src/enums/plain.rs b/avro_derive/src/enums/plain.rs new file mode 100644 index 0000000..cc77c7b --- /dev/null +++ b/avro_derive/src/enums/plain.rs @@ -0,0 +1,49 @@ +use crate::attributes::{NamedTypeOptions, VariantOptions}; +use crate::case::RenameRule; +use crate::enums::default_enum_variant; +use crate::{aliases, preserve_optional}; +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::spanned::Spanned; +use syn::{DataEnum, Fields}; + +pub fn schema_def( + container_attrs: &NamedTypeOptions, + data_enum: DataEnum, + ident_span: Span, +) -> Result<TokenStream, Vec<syn::Error>> { + let doc = preserve_optional(container_attrs.doc.as_ref()); + let enum_aliases = aliases(&container_attrs.aliases); + if data_enum.variants.iter().all(|v| Fields::Unit == v.fields) { + let default_value = default_enum_variant(&data_enum, ident_span)?; + let default = preserve_optional(default_value); + let mut symbols = Vec::new(); + for variant in &data_enum.variants { + let field_attrs = VariantOptions::new(&variant.attrs, variant.span())?; + let name = match (field_attrs.rename, container_attrs.rename_all) { + (Some(rename), _) => rename, + (None, rename_all) if !matches!(rename_all, RenameRule::None) => { + rename_all.apply_to_variant(&variant.ident.to_string()) + } + _ => variant.ident.to_string(), + }; + symbols.push(name); + } + let full_schema_name = &container_attrs.name; + Ok(quote! { + ::apache_avro::schema::Schema::Enum(apache_avro::schema::EnumSchema { + name: ::apache_avro::schema::Name::new(#full_schema_name).expect(&format!("Unable to parse enum name for schema {}", #full_schema_name)[..]), + aliases: #enum_aliases, + doc: #doc, + symbols: vec![#(#symbols.to_owned()),*], + default: #default, + attributes: Default::default(), + }) + }) + } else { + Err(vec![syn::Error::new( + ident_span, + "AvroSchema: derive does not work for enums with non unit structs", + )]) + } +} diff --git a/avro_derive/src/lib.rs b/avro_derive/src/lib.rs index d6562c8..8e590eb 100644 --- a/avro_derive/src/lib.rs +++ b/avro_derive/src/lib.rs @@ -31,16 +31,18 @@ mod attributes; mod case; +mod enums; use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{ - Attribute, DataEnum, DataStruct, DeriveInput, Expr, Field, Fields, Generics, Ident, Meta, Type, + DataStruct, DeriveInput, Expr, Field, Fields, Generics, Ident, Type, parse_macro_input, spanned::Spanned, }; +use crate::enums::get_data_enum_schema_def; use crate::{ - attributes::{FieldDefault, FieldOptions, NamedTypeOptions, VariantOptions, With}, + attributes::{FieldDefault, FieldOptions, NamedTypeOptions, With}, case::RenameRule, }; @@ -383,48 +385,6 @@ fn get_field_get_record_fields_expr( } } -/// Generate a schema definition for a enum. -fn get_data_enum_schema_def( - container_attrs: &NamedTypeOptions, - data_enum: DataEnum, - ident_span: Span, -) -> Result<TokenStream, Vec<syn::Error>> { - let doc = preserve_optional(container_attrs.doc.as_ref()); - let enum_aliases = aliases(&container_attrs.aliases); - if data_enum.variants.iter().all(|v| Fields::Unit == v.fields) { - let default_value = default_enum_variant(&data_enum, ident_span)?; - let default = preserve_optional(default_value); - let mut symbols = Vec::new(); - for variant in &data_enum.variants { - let field_attrs = VariantOptions::new(&variant.attrs, variant.span())?; - let name = match (field_attrs.rename, container_attrs.rename_all) { - (Some(rename), _) => rename, - (None, rename_all) if !matches!(rename_all, RenameRule::None) => { - rename_all.apply_to_variant(&variant.ident.to_string()) - } - _ => variant.ident.to_string(), - }; - symbols.push(name); - } - let full_schema_name = &container_attrs.name; - Ok(quote! { - ::apache_avro::schema::Schema::Enum(apache_avro::schema::EnumSchema { - name: ::apache_avro::schema::Name::new(#full_schema_name).expect(&format!("Unable to parse enum name for schema {}", #full_schema_name)[..]), - aliases: #enum_aliases, - doc: #doc, - symbols: vec![#(#symbols.to_owned()),*], - default: #default, - attributes: Default::default(), - }) - }) - } else { - Err(vec![syn::Error::new( - ident_span, - "AvroSchema: derive does not work for enums with non unit structs", - )]) - } -} - /// Takes in the Tokens of a type and returns the tokens of an expression with return type `Schema` fn type_to_schema_expr(ty: &Type) -> Result<TokenStream, Vec<syn::Error>> { match ty { @@ -492,35 +452,6 @@ fn type_to_field_default_expr(ty: &Type) -> Result<TokenStream, Vec<syn::Error>> } } -fn default_enum_variant( - data_enum: &syn::DataEnum, - error_span: Span, -) -> Result<Option<String>, Vec<syn::Error>> { - match data_enum - .variants - .iter() - .filter(|v| v.attrs.iter().any(is_default_attr)) - .collect::<Vec<_>>() - { - variants if variants.is_empty() => Ok(None), - single if single.len() == 1 => Ok(Some(single[0].ident.to_string())), - multiple => Err(vec![syn::Error::new( - error_span, - format!( - "Multiple defaults defined: {:?}", - multiple - .iter() - .map(|v| v.ident.to_string()) - .collect::<Vec<String>>() - ), - )]), - } -} - -fn is_default_attr(attr: &Attribute) -> bool { - matches!(attr, Attribute { meta: Meta::Path(path), .. } if path.get_ident().map(Ident::to_string).as_deref() == Some("default")) -} - /// Stolen from serde fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream { let compile_errors = errors.iter().map(syn::Error::to_compile_error);
