This is an automated email from the ASF dual-hosted git repository.

mgrigorov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 285f5c9  feat: Use `macrotest` to test the expanded macro code (#501)
285f5c9 is described below

commit 285f5c90d0b1b1417cc912cc853faa364134725c
Author: Kriskras99 <[email protected]>
AuthorDate: Thu Mar 5 22:24:39 2026 +0100

    feat: Use `macrotest` to test the expanded macro code (#501)
    
    * feat: Use `macrotest` to test the expanded macro code
    
    * fix: Expanded files are missing license header
    
    * fix: Steps don't share anything
    
    * fix: Use `taiki-e/install-action` to install `cargo-rdme` and 
`cargo-expand`
    
    * fix: Typo in documentation
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    * fix: Only install `cargo-rdme` on `stable-x86_64` and `cargo-expand` on 
`nightly`
    
    * fix: Apply suggestions from code review
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    * fix: Use more full paths
    
    ---------
    
    Co-authored-by: default <[email protected]>
    Co-authored-by: Martin Grigorov <[email protected]>
---
 .github/workflows/test-lang-rust-ci.yml            |  22 +-
 Cargo.lock                                         |  24 ++
 avro_derive/Cargo.toml                             |   1 +
 avro_derive/src/enums/plain.rs                     |   7 +-
 avro_derive/src/lib.rs                             | 384 +--------------------
 avro_derive/tests/expand.rs                        |  28 ++
 .../avro_3687_basic_enum_with_default.expanded.rs  |  59 ++++
 .../expanded/avro_3687_basic_enum_with_default.rs  |  28 ++
 .../avro_3709_record_field_attributes.expanded.rs  | 178 ++++++++++
 .../expanded/avro_3709_record_field_attributes.rs  |  32 ++
 .../avro_rs_207_rename_all_attribute.expanded.rs   | 180 ++++++++++
 .../expanded/avro_rs_207_rename_all_attribute.rs   |  32 ++
 ...name_attr_over_rename_all_attribute.expanded.rs | 133 +++++++
 ...rs_207_rename_attr_over_rename_all_attribute.rs |  26 ++
 .../tests/expanded/avro_rs_501_basic.expanded.rs   | 180 ++++++++++
 avro_derive/tests/expanded/avro_rs_501_basic.rs    |  32 ++
 .../expanded/avro_rs_501_namespace.expanded.rs     | 133 +++++++
 .../tests/expanded/avro_rs_501_namespace.rs        |  25 ++
 .../expanded/avro_rs_501_reference.expanded.rs     | 139 ++++++++
 .../tests/expanded/avro_rs_501_reference.rs        |  24 ++
 .../avro_rs_501_struct_with_optional.expanded.rs   | 115 ++++++
 .../expanded/avro_rs_501_struct_with_optional.rs   |  23 ++
 avro_derive/tests/expanded/mod.rs                  |  29 ++
 .../ui/avro_3687_basic_enum_with_default_twice.rs  |  30 ++
 .../avro_3687_basic_enum_with_default_twice.stderr |  19 +
 avro_derive/tests/ui/avro_rs_501_non_basic_enum.rs |  28 ++
 .../tests/ui/avro_rs_501_non_basic_enum.stderr     |   5 +
 avro_derive/tests/ui/avro_rs_501_tuple_struct.rs   |  23 ++
 .../tests/ui/avro_rs_501_tuple_struct.stderr       |   5 +
 avro_derive/tests/ui/avro_rs_501_unit_struct.rs    |  23 ++
 .../tests/ui/avro_rs_501_unit_struct.stderr        |   5 +
 licenserc.toml                                     |   1 +
 32 files changed, 1586 insertions(+), 387 deletions(-)

diff --git a/.github/workflows/test-lang-rust-ci.yml 
b/.github/workflows/test-lang-rust-ci.yml
index 431ff7c..380366b 100644
--- a/.github/workflows/test-lang-rust-ci.yml
+++ b/.github/workflows/test-lang-rust-ci.yml
@@ -61,6 +61,16 @@ jobs:
             target: aarch64-unknown-linux-gnu
 
     steps:
+      - uses: taiki-e/install-action@v2
+        if: matrix.rust == 'nightly'
+        with:
+          tool: cargo-expand
+
+      - uses: taiki-e/install-action@v2
+        if: matrix.rust == 'stable' && matrix.runner.target == 
'x86_64-unknown-linux-gnu'
+        with:
+          tool: cargo-rdme
+
       - name: Checkout
         uses: actions/checkout@v6
 
@@ -86,21 +96,11 @@ jobs:
           components: rustfmt
           targets: ${{ matrix.runner.target }}
 
-      - name: Cache cargo-rdme
-        if: matrix.rust == 'stable' && matrix.runner.target == 
'x86_64-unknown-linux-gnu'
-        uses: actions/cache@v5
-        with:
-          path: ~/.cargo-${{ matrix.rust }}/cargo-rdme
-          key: cargo-rdme-
-
       # Check if the doc cumment in avro/src/lib.rs and avro/README.md are in 
sync.
       - name: Run cargo-rdme
         # The result is environment independent so one test pattern is enough.
         if: matrix.rust == 'stable' && matrix.runner.target == 
'x86_64-unknown-linux-gnu'
-        run: |
-          cargo install --root ~/.cargo-${{ matrix.rust }}/cargo-rdme --locked 
cargo-rdme
-          export PATH=$PATH:~/.cargo-${{ matrix.rust }}/cargo-rdme/bin
-          cargo rdme --check
+        run: cargo rdme --check
 
       - name: Rust Format
         if: matrix.runner.target != 'wasm32-unknown-unknown'
diff --git a/Cargo.lock b/Cargo.lock
index bc2d38a..3accbf6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -101,6 +101,7 @@ version = "0.22.0"
 dependencies = [
  "apache-avro",
  "darling",
+ "macrotest",
  "pretty_assertions",
  "proc-macro2",
  "proptest",
@@ -537,6 +538,12 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
 
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
 [[package]]
 name = "find-msvc-tools"
 version = "0.1.9"
@@ -785,6 +792,23 @@ version = "0.4.29"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
 
+[[package]]
+name = "macrotest"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "cd198afd908012e57564b66e43e7d4d19056cec7e6232e9e6d54a1798622f81d"
+dependencies = [
+ "diff",
+ "fastrand",
+ "glob",
+ "prettyplease",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "syn",
+ "toml",
+]
+
 [[package]]
 name = "md-5"
 version = "0.10.6"
diff --git a/avro_derive/Cargo.toml b/avro_derive/Cargo.toml
index c86f9d6..c8495da 100644
--- a/avro_derive/Cargo.toml
+++ b/avro_derive/Cargo.toml
@@ -41,6 +41,7 @@ uuid = { workspace = true }
 
 [dev-dependencies]
 apache-avro = { default-features = false, path = "../avro", features = 
["derive"] }
+macrotest = { version = "1.2.1", default-features = false }
 pretty_assertions = { workspace = true }
 proptest = { default-features = false, version = "1.10.0", features = ["std"] }
 rustversion = "1.0.22"
diff --git a/avro_derive/src/enums/plain.rs b/avro_derive/src/enums/plain.rs
index 89de59c..2832c08 100644
--- a/avro_derive/src/enums/plain.rs
+++ b/avro_derive/src/enums/plain.rs
@@ -46,15 +46,14 @@ pub fn schema_def(
             };
             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)[..]),
+            
::apache_avro::schema::Schema::Enum(::apache_avro::schema::EnumSchema {
+                name,
                 aliases: #enum_aliases,
                 doc: #doc,
                 symbols: vec![#(#symbols.to_owned()),*],
                 default: #default,
-                attributes: Default::default(),
+                attributes: ::std::collections::BTreeMap::new(),
             })
         })
     } else {
diff --git a/avro_derive/src/lib.rs b/avro_derive/src/lib.rs
index 52de29b..96f997e 100644
--- a/avro_derive/src/lib.rs
+++ b/avro_derive/src/lib.rs
@@ -95,7 +95,7 @@ fn derive_avro_schema(input: DeriveInput) -> 
Result<TokenStream, Vec<syn::Error>
                 input.ident,
                 &input.generics,
                 inner,
-                quote! { None },
+                quote! { ::std::option::Option::None },
                 named_type_options.default,
             ))
         }
@@ -195,7 +195,7 @@ fn get_struct_schema_def(
                     continue;
                 }
                 let default_value = match field_attrs.default {
-                    FieldDefault::Disabled => quote! { None },
+                    FieldDefault::Disabled => quote! { 
::std::option::Option::None },
                     FieldDefault::Trait => 
type_to_field_default_expr(&field.ty)?,
                     FieldDefault::Value(default_value) => {
                         let _: serde_json::Value = 
serde_json::from_str(&default_value[..])
@@ -206,7 +206,7 @@ fn get_struct_schema_def(
                                 )]
                             })?;
                         quote! {
-                            
Some(::serde_json::from_str(#default_value).expect(format!("Invalid JSON: 
{:?}", #default_value).as_str()))
+                            
::std::option::Option::Some(::serde_json::from_str(#default_value).expect("Unreachable!
 This parsed at compile time!"))
                         }
                     }
                 };
@@ -219,7 +219,7 @@ fn get_struct_schema_def(
                         default: #default_value,
                         aliases: #aliases,
                         schema: #schema_expr,
-                        custom_attributes: Default::default(),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
                     });
                 });
             }
@@ -248,30 +248,30 @@ fn get_struct_schema_def(
 
     let schema_def = quote! {
         {
-            let mut schema_fields = Vec::with_capacity(#minimum_fields);
+            let mut schema_fields = 
::std::vec::Vec::with_capacity(#minimum_fields);
             #(#record_field_exprs)*
             let schema_field_set: ::std::collections::HashSet<_> = 
schema_fields.iter().map(|rf| &rf.name).collect();
             assert_eq!(schema_fields.len(), schema_field_set.len(), "Duplicate 
field names found: {schema_fields:?}");
             let name = 
::apache_avro::schema::Name::new(#full_schema_name).expect(&format!("Unable to 
parse struct name for schema {}", #full_schema_name)[..]);
-            let lookup: std::collections::BTreeMap<String, usize> = 
schema_fields
+            let lookup: ::std::collections::BTreeMap<String, usize> = 
schema_fields
                 .iter()
                 .enumerate()
                 .map(|(position, field)| (field.name.to_owned(), position))
                 .collect();
-            
::apache_avro::schema::Schema::Record(apache_avro::schema::RecordSchema {
+            
::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema {
                 name,
                 aliases: #record_aliases,
                 doc: #record_doc,
                 fields: schema_fields,
                 lookup,
-                attributes: Default::default(),
+                attributes: ::std::collections::BTreeMap::new(),
             })
         }
     };
     let record_fields = quote! {
-        let mut schema_fields = Vec::with_capacity(#minimum_fields);
+        let mut schema_fields = 
::std::vec::Vec::with_capacity(#minimum_fields);
         #(#record_field_exprs)*
-        Some(schema_fields)
+        ::std::option::Option::Some(schema_fields)
     };
 
     Ok((schema_def, record_fields))
@@ -455,8 +455,8 @@ fn to_compile_errors(errors: Vec<syn::Error>) -> 
proc_macro2::TokenStream {
 
 fn preserve_optional(op: Option<impl quote::ToTokens>) -> TokenStream {
     match op {
-        Some(tt) => quote! {Some(#tt.into())},
-        None => quote! {None},
+        Some(tt) => quote! {::std::option::Option::Some(#tt.into())},
+        None => quote! {::std::option::Option::None},
     }
 }
 
@@ -489,370 +489,10 @@ mod tests {
     use super::*;
     use pretty_assertions::assert_eq;
 
-    #[test]
-    fn basic_case() {
-        let test_struct = quote! {
-            struct A {
-                a: i32,
-                b: String
-            }
-        };
-
-        match syn::parse2::<DeriveInput>(test_struct) {
-            Ok(input) => {
-                assert!(derive_avro_schema(input).is_ok())
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
-
-    #[test]
-    fn tuple_struct_unsupported() {
-        let test_tuple_struct = quote! {
-            struct B (i32, String);
-        };
-
-        match syn::parse2::<DeriveInput>(test_tuple_struct) {
-            Ok(input) => {
-                assert!(derive_avro_schema(input).is_err())
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
-
-    #[test]
-    fn unit_struct_unsupported() {
-        let test_tuple_struct = quote! {
-            struct AbsoluteUnit;
-        };
-
-        match syn::parse2::<DeriveInput>(test_tuple_struct) {
-            Ok(input) => {
-                assert!(derive_avro_schema(input).is_err())
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
-
-    #[test]
-    fn struct_with_optional() {
-        let struct_with_optional = quote! {
-            struct Test4 {
-                a : Option<i32>
-            }
-        };
-        match syn::parse2::<DeriveInput>(struct_with_optional) {
-            Ok(input) => {
-                assert!(derive_avro_schema(input).is_ok())
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
-
-    #[test]
-    fn test_basic_enum() {
-        let basic_enum = quote! {
-            enum Basic {
-                A,
-                B,
-                C,
-                D
-            }
-        };
-        match syn::parse2::<DeriveInput>(basic_enum) {
-            Ok(input) => {
-                assert!(derive_avro_schema(input).is_ok())
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
-
-    #[test]
-    fn avro_3687_basic_enum_with_default() {
-        let basic_enum = quote! {
-            enum Basic {
-                #[default]
-                A,
-                B,
-                C,
-                D
-            }
-        };
-        match syn::parse2::<DeriveInput>(basic_enum) {
-            Ok(input) => {
-                let derived = derive_avro_schema(input);
-                assert!(derived.is_ok());
-                assert_eq!(derived.unwrap().to_string(), quote! {
-                    #[automatically_derived]
-                    impl ::apache_avro::AvroSchemaComponent for Basic {
-                        fn get_schema_in_ctxt(
-                            named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
-                            enclosing_namespace: 
::apache_avro::schema::NamespaceRef
-                        ) -> ::apache_avro::schema::Schema {
-                            let name = 
::apache_avro::schema::Name::new_with_enclosing_namespace("Basic", 
enclosing_namespace)
-                                .expect(concat!("Unable to parse schema name 
", "Basic"));
-                            if named_schemas.contains(&name) {
-                                ::apache_avro::schema::Schema::Ref { name }
-                            } else {
-                                let enclosing_namespace = name.namespace();
-                                named_schemas.insert(name.clone());
-                                
::apache_avro::schema::Schema::Enum(apache_avro::schema::EnumSchema {
-                                    name: 
::apache_avro::schema::Name::new("Basic").expect(
-                                        &format!("Unable to parse enum name 
for schema {}", "Basic")[..]
-                                    ),
-                                    aliases: ::std::option::Option::None,
-                                    doc: None,
-                                    symbols: vec![
-                                        "A".to_owned(),
-                                        "B".to_owned(),
-                                        "C".to_owned(),
-                                        "D".to_owned()
-                                    ],
-                                    default: Some("A".into()),
-                                    attributes: Default::default(),
-                                })
-                            }
-                        }
-
-                        fn get_record_fields_in_ctxt(
-                            named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
-                            enclosing_namespace: 
::apache_avro::schema::NamespaceRef
-                        ) -> ::std::option::Option 
<::std::vec::Vec<::apache_avro::schema::RecordField>> {
-                            None
-                        }
-
-                        fn field_default () -> 
::std::option::Option<::serde_json::Value> {
-                            ::std::option::Option::None
-                        }
-                    }
-                }.to_string());
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
-
-    #[test]
-    fn avro_3687_basic_enum_with_default_twice() {
-        let non_basic_enum = quote! {
-            enum Basic {
-                #[default]
-                A,
-                B,
-                #[default]
-                C,
-                D
-            }
-        };
-        match syn::parse2::<DeriveInput>(non_basic_enum) {
-            Ok(input) => match derive_avro_schema(input) {
-                Ok(_) => {
-                    panic!("Should not be able to derive schema for enum with 
multiple defaults")
-                }
-                Err(errors) => {
-                    assert_eq!(errors.len(), 1);
-                    assert_eq!(
-                        errors[0].to_string(),
-                        r#"Multiple defaults defined: ["A", "C"]"#
-                    );
-                }
-            },
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
-
-    #[test]
-    fn test_non_basic_enum() {
-        let non_basic_enum = quote! {
-            enum Basic {
-                A(i32),
-                B,
-                C,
-                D
-            }
-        };
-        match syn::parse2::<DeriveInput>(non_basic_enum) {
-            Ok(input) => {
-                assert!(derive_avro_schema(input).is_err())
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
-
-    #[test]
-    fn test_namespace() {
-        let test_struct = quote! {
-            #[avro(namespace = "namespace.testing")]
-            struct A {
-                a: i32,
-                b: String
-            }
-        };
-
-        match syn::parse2::<DeriveInput>(test_struct) {
-            Ok(input) => {
-                let schema_token_stream = derive_avro_schema(input);
-                assert!(&schema_token_stream.is_ok());
-                assert!(
-                    schema_token_stream
-                        .unwrap()
-                        .to_string()
-                        .contains("namespace.testing")
-                )
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
-
-    #[test]
-    fn test_reference() {
-        let test_reference_struct = quote! {
-            struct A<'a> {
-                a: &'a Vec<i32>,
-                b: &'static str
-            }
-        };
-
-        match syn::parse2::<DeriveInput>(test_reference_struct) {
-            Ok(input) => {
-                assert!(derive_avro_schema(input).is_ok())
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
-
     #[test]
     fn test_trait_cast() {
         
assert_eq!(type_to_schema_expr(&syn::parse2::<Type>(quote!{i32}).unwrap()).unwrap().to_string(),
 quote!{<i32 as :: 
apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, 
enclosing_namespace)}.to_string());
         
assert_eq!(type_to_schema_expr(&syn::parse2::<Type>(quote!{Vec<T>}).unwrap()).unwrap().to_string(),
 quote!{<Vec<T> as :: 
apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, 
enclosing_namespace)}.to_string());
         
assert_eq!(type_to_schema_expr(&syn::parse2::<Type>(quote!{AnyType}).unwrap()).unwrap().to_string(),
 quote!{<AnyType as :: 
apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, 
enclosing_namespace)}.to_string());
     }
-
-    #[test]
-    fn test_avro_3709_record_field_attributes() {
-        let test_struct = quote! {
-            struct A {
-                #[serde(alias = "a1", alias = "a2", rename = "a3")]
-                #[avro(doc = "a doc", default = "123")]
-                a: i32
-            }
-        };
-
-        match syn::parse2::<DeriveInput>(test_struct) {
-            Ok(input) => {
-                let schema_res = derive_avro_schema(input);
-                let expected_token_stream = r#"# [automatically_derived] impl 
:: apache_avro :: AvroSchemaComponent for A { fn get_schema_in_ctxt 
(named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: 
schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: 
NamespaceRef) -> :: apache_avro :: schema :: Schema { let name = :: apache_avro 
:: schema :: Name :: new_with_enclosing_namespace ("A" , enclosing_namespace) . 
expect (concat ! ("Unable to parse schema [...]
-                let schema_token_stream = schema_res.unwrap().to_string();
-                assert_eq!(schema_token_stream, expected_token_stream);
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-
-        let test_enum = quote! {
-            enum A {
-                #[serde(rename = "A3")]
-                Item1,
-            }
-        };
-
-        match syn::parse2::<DeriveInput>(test_enum) {
-            Ok(input) => {
-                let schema_res = derive_avro_schema(input);
-                let expected_token_stream = r#"# [automatically_derived] impl 
:: apache_avro :: AvroSchemaComponent for A { fn get_schema_in_ctxt 
(named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: 
schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: 
NamespaceRef) -> :: apache_avro :: schema :: Schema { let name = :: apache_avro 
:: schema :: Name :: new_with_enclosing_namespace ("A" , enclosing_namespace) . 
expect (concat ! ("Unable to parse schema [...]
-                let schema_token_stream = schema_res.unwrap().to_string();
-                assert_eq!(schema_token_stream, expected_token_stream);
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
-
-    #[test]
-    fn test_avro_rs_207_rename_all_attribute() {
-        let test_struct = quote! {
-            #[serde(rename_all="SCREAMING_SNAKE_CASE")]
-            struct A {
-                item: i32,
-                double_item: i32
-            }
-        };
-
-        match syn::parse2::<DeriveInput>(test_struct) {
-            Ok(input) => {
-                let schema_res = derive_avro_schema(input);
-                let expected_token_stream = r#"# [automatically_derived] impl 
:: apache_avro :: AvroSchemaComponent for A { fn get_schema_in_ctxt 
(named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: 
schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: 
NamespaceRef) -> :: apache_avro :: schema :: Schema { let name = :: apache_avro 
:: schema :: Name :: new_with_enclosing_namespace ("A" , enclosing_namespace) . 
expect (concat ! ("Unable to parse schema [...]
-                let schema_token_stream = schema_res.unwrap().to_string();
-                assert_eq!(schema_token_stream, expected_token_stream);
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-
-        let test_enum = quote! {
-            #[serde(rename_all="SCREAMING_SNAKE_CASE")]
-            enum B {
-                Item,
-                DoubleItem,
-            }
-        };
-
-        match syn::parse2::<DeriveInput>(test_enum) {
-            Ok(input) => {
-                let schema_res = derive_avro_schema(input);
-                let expected_token_stream = r#"# [automatically_derived] impl 
:: apache_avro :: AvroSchemaComponent for B { fn get_schema_in_ctxt 
(named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: 
schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: 
NamespaceRef) -> :: apache_avro :: schema :: Schema { let name = :: apache_avro 
:: schema :: Name :: new_with_enclosing_namespace ("B" , enclosing_namespace) . 
expect (concat ! ("Unable to parse schema [...]
-                let schema_token_stream = schema_res.unwrap().to_string();
-                assert_eq!(schema_token_stream, expected_token_stream);
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
-
-    #[test]
-    fn test_avro_rs_207_rename_attr_has_priority_over_rename_all_attribute() {
-        let test_struct = quote! {
-            #[serde(rename_all="SCREAMING_SNAKE_CASE")]
-            struct A {
-                item: i32,
-                #[serde(rename="DoubleItem")]
-                double_item: i32
-            }
-        };
-
-        match syn::parse2::<DeriveInput>(test_struct) {
-            Ok(input) => {
-                let schema_res = derive_avro_schema(input);
-                let expected_token_stream = r#"# [automatically_derived] impl 
:: apache_avro :: AvroSchemaComponent for A { fn get_schema_in_ctxt 
(named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: 
schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: 
NamespaceRef) -> :: apache_avro :: schema :: Schema { let name = :: apache_avro 
:: schema :: Name :: new_with_enclosing_namespace ("A" , enclosing_namespace) . 
expect (concat ! ("Unable to parse schema [...]
-                let schema_token_stream = schema_res.unwrap().to_string();
-                assert_eq!(schema_token_stream, expected_token_stream);
-            }
-            Err(error) => panic!(
-                "Failed to parse as derive input when it should be able to. 
Error: {error:?}"
-            ),
-        };
-    }
 }
diff --git a/avro_derive/tests/expand.rs b/avro_derive/tests/expand.rs
new file mode 100644
index 0000000..6ff76b7
--- /dev/null
+++ b/avro_derive/tests/expand.rs
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// By including the (unexpanded) modules, we can also test that the
+// generated code is valid.
+mod expanded;
+
+/// These tests only run on nightly as the output can change per compiler 
version.
+/// Use `MACROTEST=overwrite cargo +nightly test expand` to re-generate them
+#[rustversion::attr(not(nightly), ignore)]
+#[test]
+fn expand() {
+    macrotest::expand("tests/expanded/avro_*.rs");
+}
diff --git 
a/avro_derive/tests/expanded/avro_3687_basic_enum_with_default.expanded.rs 
b/avro_derive/tests/expanded/avro_3687_basic_enum_with_default.expanded.rs
new file mode 100644
index 0000000..62fcff7
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_3687_basic_enum_with_default.expanded.rs
@@ -0,0 +1,59 @@
+use apache_avro::AvroSchema;
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+enum Basic {
+    #[default]
+    A,
+    B,
+    C,
+    D,
+}
+#[automatically_derived]
+impl ::apache_avro::AvroSchemaComponent for Basic {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> ::apache_avro::schema::Schema {
+        let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
+                "Basic",
+                enclosing_namespace,
+            )
+            .expect("Unable to parse schema name Basic");
+        if named_schemas.contains(&name) {
+            ::apache_avro::schema::Schema::Ref {
+                name,
+            }
+        } else {
+            let enclosing_namespace = name.namespace();
+            named_schemas.insert(name.clone());
+            
::apache_avro::schema::Schema::Enum(::apache_avro::schema::EnumSchema {
+                name,
+                aliases: ::std::option::Option::None,
+                doc: ::std::option::Option::None,
+                symbols: ::alloc::boxed::box_assume_init_into_vec_unsafe(
+                    ::alloc::intrinsics::write_box_via_move(
+                        ::alloc::boxed::Box::new_uninit(),
+                        ["A".to_owned(), "B".to_owned(), "C".to_owned(), 
"D".to_owned()],
+                    ),
+                ),
+                default: ::std::option::Option::Some("A".into()),
+                attributes: ::std::collections::BTreeMap::new(),
+            })
+        }
+    }
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> 
::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
+        ::std::option::Option::None
+    }
+    fn field_default() -> ::std::option::Option<::serde_json::Value> {
+        ::std::option::Option::None
+    }
+}
+#[automatically_derived]
+impl ::core::default::Default for Basic {
+    #[inline]
+    fn default() -> Basic {
+        Self::A
+    }
+}
diff --git a/avro_derive/tests/expanded/avro_3687_basic_enum_with_default.rs 
b/avro_derive/tests/expanded/avro_3687_basic_enum_with_default.rs
new file mode 100644
index 0000000..21f2617
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_3687_basic_enum_with_default.rs
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema, Default)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+enum Basic {
+    #[default]
+    A,
+    B,
+    C,
+    D,
+}
diff --git 
a/avro_derive/tests/expanded/avro_3709_record_field_attributes.expanded.rs 
b/avro_derive/tests/expanded/avro_3709_record_field_attributes.expanded.rs
new file mode 100644
index 0000000..0d35f4b
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_3709_record_field_attributes.expanded.rs
@@ -0,0 +1,178 @@
+use apache_avro::AvroSchema;
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+struct A {
+    #[serde(alias = "a1", alias = "a2", rename = "a3")]
+    #[avro(doc = "a doc", default = "123")]
+    a: i32,
+}
+#[automatically_derived]
+impl ::apache_avro::AvroSchemaComponent for A {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> ::apache_avro::schema::Schema {
+        let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
+                "A",
+                enclosing_namespace,
+            )
+            .expect("Unable to parse schema name A");
+        if named_schemas.contains(&name) {
+            ::apache_avro::schema::Schema::Ref {
+                name,
+            }
+        } else {
+            let enclosing_namespace = name.namespace();
+            named_schemas.insert(name.clone());
+            {
+                let mut schema_fields = ::std::vec::Vec::with_capacity(1usize);
+                schema_fields
+                    .push(::apache_avro::schema::RecordField {
+                        name: "a3".to_string(),
+                        doc: ::std::option::Option::Some("a doc".into()),
+                        default: ::std::option::Option::Some(
+                            ::serde_json::from_str("123")
+                                .expect("Unreachable! This parsed at compile 
time!"),
+                        ),
+                        aliases: 
::alloc::boxed::box_assume_init_into_vec_unsafe(
+                            ::alloc::intrinsics::write_box_via_move(
+                                ::alloc::boxed::Box::new_uninit(),
+                                [
+                                    "a1".try_into().expect("Alias is invalid"),
+                                    "a2".try_into().expect("Alias is invalid"),
+                                ],
+                            ),
+                        ),
+                        schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                            named_schemas,
+                            enclosing_namespace,
+                        ),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
+                    });
+                let schema_field_set: ::std::collections::HashSet<_> = 
schema_fields
+                    .iter()
+                    .map(|rf| &rf.name)
+                    .collect();
+                match (&schema_fields.len(), &schema_field_set.len()) {
+                    (left_val, right_val) => {
+                        if !(*left_val == *right_val) {
+                            let kind = ::core::panicking::AssertKind::Eq;
+                            ::core::panicking::assert_failed(
+                                kind,
+                                &*left_val,
+                                &*right_val,
+                                ::core::option::Option::Some(
+                                    format_args!(
+                                        "Duplicate field names found: {0:?}", 
schema_fields,
+                                    ),
+                                ),
+                            );
+                        }
+                    }
+                };
+                let name = ::apache_avro::schema::Name::new("A")
+                    .expect(
+                        &::alloc::__export::must_use({
+                            ::alloc::fmt::format(
+                                format_args!(
+                                    "Unable to parse struct name for schema 
{0}", "A",
+                                ),
+                            )
+                        })[..],
+                    );
+                let lookup: ::std::collections::BTreeMap<String, usize> = 
schema_fields
+                    .iter()
+                    .enumerate()
+                    .map(|(position, field)| (field.name.to_owned(), position))
+                    .collect();
+                
::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema {
+                    name,
+                    aliases: ::std::option::Option::None,
+                    doc: ::std::option::Option::None,
+                    fields: schema_fields,
+                    lookup,
+                    attributes: ::std::collections::BTreeMap::new(),
+                })
+            }
+        }
+    }
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> 
::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
+        let mut schema_fields = ::std::vec::Vec::with_capacity(1usize);
+        schema_fields
+            .push(::apache_avro::schema::RecordField {
+                name: "a3".to_string(),
+                doc: ::std::option::Option::Some("a doc".into()),
+                default: ::std::option::Option::Some(
+                    ::serde_json::from_str("123")
+                        .expect("Unreachable! This parsed at compile time!"),
+                ),
+                aliases: ::alloc::boxed::box_assume_init_into_vec_unsafe(
+                    ::alloc::intrinsics::write_box_via_move(
+                        ::alloc::boxed::Box::new_uninit(),
+                        [
+                            "a1".try_into().expect("Alias is invalid"),
+                            "a2".try_into().expect("Alias is invalid"),
+                        ],
+                    ),
+                ),
+                schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                    named_schemas,
+                    enclosing_namespace,
+                ),
+                custom_attributes: ::std::collections::BTreeMap::new(),
+            });
+        ::std::option::Option::Some(schema_fields)
+    }
+    fn field_default() -> ::std::option::Option<::serde_json::Value> {
+        ::std::option::Option::None
+    }
+}
+enum B {
+    #[serde(rename = "A3")]
+    Item1,
+}
+#[automatically_derived]
+impl ::apache_avro::AvroSchemaComponent for B {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> ::apache_avro::schema::Schema {
+        let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
+                "B",
+                enclosing_namespace,
+            )
+            .expect("Unable to parse schema name B");
+        if named_schemas.contains(&name) {
+            ::apache_avro::schema::Schema::Ref {
+                name,
+            }
+        } else {
+            let enclosing_namespace = name.namespace();
+            named_schemas.insert(name.clone());
+            
::apache_avro::schema::Schema::Enum(::apache_avro::schema::EnumSchema {
+                name,
+                aliases: ::std::option::Option::None,
+                doc: ::std::option::Option::None,
+                symbols: ::alloc::boxed::box_assume_init_into_vec_unsafe(
+                    ::alloc::intrinsics::write_box_via_move(
+                        ::alloc::boxed::Box::new_uninit(),
+                        ["A3".to_owned()],
+                    ),
+                ),
+                default: ::std::option::Option::None,
+                attributes: ::std::collections::BTreeMap::new(),
+            })
+        }
+    }
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> 
::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
+        ::std::option::Option::None
+    }
+    fn field_default() -> ::std::option::Option<::serde_json::Value> {
+        ::std::option::Option::None
+    }
+}
diff --git a/avro_derive/tests/expanded/avro_3709_record_field_attributes.rs 
b/avro_derive/tests/expanded/avro_3709_record_field_attributes.rs
new file mode 100644
index 0000000..240d71a
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_3709_record_field_attributes.rs
@@ -0,0 +1,32 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+struct A {
+    #[serde(alias = "a1", alias = "a2", rename = "a3")]
+    #[avro(doc = "a doc", default = "123")]
+    a: i32,
+}
+
+#[derive(AvroSchema)]
+enum B {
+    #[serde(rename = "A3")]
+    Item1,
+}
diff --git 
a/avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.expanded.rs 
b/avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.expanded.rs
new file mode 100644
index 0000000..d80a114
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.expanded.rs
@@ -0,0 +1,180 @@
+use apache_avro::AvroSchema;
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+struct A {
+    item: i32,
+    double_item: i32,
+}
+#[automatically_derived]
+impl ::apache_avro::AvroSchemaComponent for A {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> ::apache_avro::schema::Schema {
+        let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
+                "A",
+                enclosing_namespace,
+            )
+            .expect("Unable to parse schema name A");
+        if named_schemas.contains(&name) {
+            ::apache_avro::schema::Schema::Ref {
+                name,
+            }
+        } else {
+            let enclosing_namespace = name.namespace();
+            named_schemas.insert(name.clone());
+            {
+                let mut schema_fields = ::std::vec::Vec::with_capacity(2usize);
+                schema_fields
+                    .push(::apache_avro::schema::RecordField {
+                        name: "ITEM".to_string(),
+                        doc: ::std::option::Option::None,
+                        default: <i32 as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                        aliases: ::std::vec::Vec::new(),
+                        schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                            named_schemas,
+                            enclosing_namespace,
+                        ),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
+                    });
+                schema_fields
+                    .push(::apache_avro::schema::RecordField {
+                        name: "DOUBLE_ITEM".to_string(),
+                        doc: ::std::option::Option::None,
+                        default: <i32 as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                        aliases: ::std::vec::Vec::new(),
+                        schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                            named_schemas,
+                            enclosing_namespace,
+                        ),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
+                    });
+                let schema_field_set: ::std::collections::HashSet<_> = 
schema_fields
+                    .iter()
+                    .map(|rf| &rf.name)
+                    .collect();
+                match (&schema_fields.len(), &schema_field_set.len()) {
+                    (left_val, right_val) => {
+                        if !(*left_val == *right_val) {
+                            let kind = ::core::panicking::AssertKind::Eq;
+                            ::core::panicking::assert_failed(
+                                kind,
+                                &*left_val,
+                                &*right_val,
+                                ::core::option::Option::Some(
+                                    format_args!(
+                                        "Duplicate field names found: {0:?}", 
schema_fields,
+                                    ),
+                                ),
+                            );
+                        }
+                    }
+                };
+                let name = ::apache_avro::schema::Name::new("A")
+                    .expect(
+                        &::alloc::__export::must_use({
+                            ::alloc::fmt::format(
+                                format_args!(
+                                    "Unable to parse struct name for schema 
{0}", "A",
+                                ),
+                            )
+                        })[..],
+                    );
+                let lookup: ::std::collections::BTreeMap<String, usize> = 
schema_fields
+                    .iter()
+                    .enumerate()
+                    .map(|(position, field)| (field.name.to_owned(), position))
+                    .collect();
+                
::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema {
+                    name,
+                    aliases: ::std::option::Option::None,
+                    doc: ::std::option::Option::None,
+                    fields: schema_fields,
+                    lookup,
+                    attributes: ::std::collections::BTreeMap::new(),
+                })
+            }
+        }
+    }
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> 
::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
+        let mut schema_fields = ::std::vec::Vec::with_capacity(2usize);
+        schema_fields
+            .push(::apache_avro::schema::RecordField {
+                name: "ITEM".to_string(),
+                doc: ::std::option::Option::None,
+                default: <i32 as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                aliases: ::std::vec::Vec::new(),
+                schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                    named_schemas,
+                    enclosing_namespace,
+                ),
+                custom_attributes: ::std::collections::BTreeMap::new(),
+            });
+        schema_fields
+            .push(::apache_avro::schema::RecordField {
+                name: "DOUBLE_ITEM".to_string(),
+                doc: ::std::option::Option::None,
+                default: <i32 as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                aliases: ::std::vec::Vec::new(),
+                schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                    named_schemas,
+                    enclosing_namespace,
+                ),
+                custom_attributes: ::std::collections::BTreeMap::new(),
+            });
+        ::std::option::Option::Some(schema_fields)
+    }
+    fn field_default() -> ::std::option::Option<::serde_json::Value> {
+        ::std::option::Option::None
+    }
+}
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+enum B {
+    Item,
+    DoubleItem,
+}
+#[automatically_derived]
+impl ::apache_avro::AvroSchemaComponent for B {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> ::apache_avro::schema::Schema {
+        let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
+                "B",
+                enclosing_namespace,
+            )
+            .expect("Unable to parse schema name B");
+        if named_schemas.contains(&name) {
+            ::apache_avro::schema::Schema::Ref {
+                name,
+            }
+        } else {
+            let enclosing_namespace = name.namespace();
+            named_schemas.insert(name.clone());
+            
::apache_avro::schema::Schema::Enum(::apache_avro::schema::EnumSchema {
+                name,
+                aliases: ::std::option::Option::None,
+                doc: ::std::option::Option::None,
+                symbols: ::alloc::boxed::box_assume_init_into_vec_unsafe(
+                    ::alloc::intrinsics::write_box_via_move(
+                        ::alloc::boxed::Box::new_uninit(),
+                        ["ITEM".to_owned(), "DOUBLE_ITEM".to_owned()],
+                    ),
+                ),
+                default: ::std::option::Option::None,
+                attributes: ::std::collections::BTreeMap::new(),
+            })
+        }
+    }
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> 
::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
+        ::std::option::Option::None
+    }
+    fn field_default() -> ::std::option::Option<::serde_json::Value> {
+        ::std::option::Option::None
+    }
+}
diff --git a/avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.rs 
b/avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.rs
new file mode 100644
index 0000000..6657f60
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.rs
@@ -0,0 +1,32 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+struct A {
+    item: i32,
+    double_item: i32,
+}
+
+#[derive(AvroSchema)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+enum B {
+    Item,
+    DoubleItem,
+}
diff --git 
a/avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.expanded.rs
 
b/avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.expanded.rs
new file mode 100644
index 0000000..0689cb1
--- /dev/null
+++ 
b/avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.expanded.rs
@@ -0,0 +1,133 @@
+use apache_avro::AvroSchema;
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+struct A {
+    item: i32,
+    #[serde(rename = "DoubleItem")]
+    double_item: i32,
+}
+#[automatically_derived]
+impl ::apache_avro::AvroSchemaComponent for A {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> ::apache_avro::schema::Schema {
+        let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
+                "A",
+                enclosing_namespace,
+            )
+            .expect("Unable to parse schema name A");
+        if named_schemas.contains(&name) {
+            ::apache_avro::schema::Schema::Ref {
+                name,
+            }
+        } else {
+            let enclosing_namespace = name.namespace();
+            named_schemas.insert(name.clone());
+            {
+                let mut schema_fields = ::std::vec::Vec::with_capacity(2usize);
+                schema_fields
+                    .push(::apache_avro::schema::RecordField {
+                        name: "ITEM".to_string(),
+                        doc: ::std::option::Option::None,
+                        default: <i32 as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                        aliases: ::std::vec::Vec::new(),
+                        schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                            named_schemas,
+                            enclosing_namespace,
+                        ),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
+                    });
+                schema_fields
+                    .push(::apache_avro::schema::RecordField {
+                        name: "DoubleItem".to_string(),
+                        doc: ::std::option::Option::None,
+                        default: <i32 as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                        aliases: ::std::vec::Vec::new(),
+                        schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                            named_schemas,
+                            enclosing_namespace,
+                        ),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
+                    });
+                let schema_field_set: ::std::collections::HashSet<_> = 
schema_fields
+                    .iter()
+                    .map(|rf| &rf.name)
+                    .collect();
+                match (&schema_fields.len(), &schema_field_set.len()) {
+                    (left_val, right_val) => {
+                        if !(*left_val == *right_val) {
+                            let kind = ::core::panicking::AssertKind::Eq;
+                            ::core::panicking::assert_failed(
+                                kind,
+                                &*left_val,
+                                &*right_val,
+                                ::core::option::Option::Some(
+                                    format_args!(
+                                        "Duplicate field names found: {0:?}", 
schema_fields,
+                                    ),
+                                ),
+                            );
+                        }
+                    }
+                };
+                let name = ::apache_avro::schema::Name::new("A")
+                    .expect(
+                        &::alloc::__export::must_use({
+                            ::alloc::fmt::format(
+                                format_args!(
+                                    "Unable to parse struct name for schema 
{0}", "A",
+                                ),
+                            )
+                        })[..],
+                    );
+                let lookup: ::std::collections::BTreeMap<String, usize> = 
schema_fields
+                    .iter()
+                    .enumerate()
+                    .map(|(position, field)| (field.name.to_owned(), position))
+                    .collect();
+                
::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema {
+                    name,
+                    aliases: ::std::option::Option::None,
+                    doc: ::std::option::Option::None,
+                    fields: schema_fields,
+                    lookup,
+                    attributes: ::std::collections::BTreeMap::new(),
+                })
+            }
+        }
+    }
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> 
::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
+        let mut schema_fields = ::std::vec::Vec::with_capacity(2usize);
+        schema_fields
+            .push(::apache_avro::schema::RecordField {
+                name: "ITEM".to_string(),
+                doc: ::std::option::Option::None,
+                default: <i32 as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                aliases: ::std::vec::Vec::new(),
+                schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                    named_schemas,
+                    enclosing_namespace,
+                ),
+                custom_attributes: ::std::collections::BTreeMap::new(),
+            });
+        schema_fields
+            .push(::apache_avro::schema::RecordField {
+                name: "DoubleItem".to_string(),
+                doc: ::std::option::Option::None,
+                default: <i32 as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                aliases: ::std::vec::Vec::new(),
+                schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                    named_schemas,
+                    enclosing_namespace,
+                ),
+                custom_attributes: ::std::collections::BTreeMap::new(),
+            });
+        ::std::option::Option::Some(schema_fields)
+    }
+    fn field_default() -> ::std::option::Option<::serde_json::Value> {
+        ::std::option::Option::None
+    }
+}
diff --git 
a/avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.rs
 
b/avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.rs
new file mode 100644
index 0000000..283b223
--- /dev/null
+++ 
b/avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.rs
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+struct A {
+    item: i32,
+    #[serde(rename = "DoubleItem")]
+    double_item: i32,
+}
diff --git a/avro_derive/tests/expanded/avro_rs_501_basic.expanded.rs 
b/avro_derive/tests/expanded/avro_rs_501_basic.expanded.rs
new file mode 100644
index 0000000..34576a5
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_rs_501_basic.expanded.rs
@@ -0,0 +1,180 @@
+use apache_avro::AvroSchema;
+struct A {
+    a: i32,
+    b: String,
+}
+#[automatically_derived]
+impl ::apache_avro::AvroSchemaComponent for A {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> ::apache_avro::schema::Schema {
+        let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
+                "A",
+                enclosing_namespace,
+            )
+            .expect("Unable to parse schema name A");
+        if named_schemas.contains(&name) {
+            ::apache_avro::schema::Schema::Ref {
+                name,
+            }
+        } else {
+            let enclosing_namespace = name.namespace();
+            named_schemas.insert(name.clone());
+            {
+                let mut schema_fields = ::std::vec::Vec::with_capacity(2usize);
+                schema_fields
+                    .push(::apache_avro::schema::RecordField {
+                        name: "a".to_string(),
+                        doc: ::std::option::Option::None,
+                        default: <i32 as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                        aliases: ::std::vec::Vec::new(),
+                        schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                            named_schemas,
+                            enclosing_namespace,
+                        ),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
+                    });
+                schema_fields
+                    .push(::apache_avro::schema::RecordField {
+                        name: "b".to_string(),
+                        doc: ::std::option::Option::None,
+                        default: <String as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                        aliases: ::std::vec::Vec::new(),
+                        schema: <String as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                            named_schemas,
+                            enclosing_namespace,
+                        ),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
+                    });
+                let schema_field_set: ::std::collections::HashSet<_> = 
schema_fields
+                    .iter()
+                    .map(|rf| &rf.name)
+                    .collect();
+                match (&schema_fields.len(), &schema_field_set.len()) {
+                    (left_val, right_val) => {
+                        if !(*left_val == *right_val) {
+                            let kind = ::core::panicking::AssertKind::Eq;
+                            ::core::panicking::assert_failed(
+                                kind,
+                                &*left_val,
+                                &*right_val,
+                                ::core::option::Option::Some(
+                                    format_args!(
+                                        "Duplicate field names found: {0:?}", 
schema_fields,
+                                    ),
+                                ),
+                            );
+                        }
+                    }
+                };
+                let name = ::apache_avro::schema::Name::new("A")
+                    .expect(
+                        &::alloc::__export::must_use({
+                            ::alloc::fmt::format(
+                                format_args!(
+                                    "Unable to parse struct name for schema 
{0}", "A",
+                                ),
+                            )
+                        })[..],
+                    );
+                let lookup: ::std::collections::BTreeMap<String, usize> = 
schema_fields
+                    .iter()
+                    .enumerate()
+                    .map(|(position, field)| (field.name.to_owned(), position))
+                    .collect();
+                
::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema {
+                    name,
+                    aliases: ::std::option::Option::None,
+                    doc: ::std::option::Option::None,
+                    fields: schema_fields,
+                    lookup,
+                    attributes: ::std::collections::BTreeMap::new(),
+                })
+            }
+        }
+    }
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> 
::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
+        let mut schema_fields = ::std::vec::Vec::with_capacity(2usize);
+        schema_fields
+            .push(::apache_avro::schema::RecordField {
+                name: "a".to_string(),
+                doc: ::std::option::Option::None,
+                default: <i32 as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                aliases: ::std::vec::Vec::new(),
+                schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                    named_schemas,
+                    enclosing_namespace,
+                ),
+                custom_attributes: ::std::collections::BTreeMap::new(),
+            });
+        schema_fields
+            .push(::apache_avro::schema::RecordField {
+                name: "b".to_string(),
+                doc: ::std::option::Option::None,
+                default: <String as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                aliases: ::std::vec::Vec::new(),
+                schema: <String as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                    named_schemas,
+                    enclosing_namespace,
+                ),
+                custom_attributes: ::std::collections::BTreeMap::new(),
+            });
+        ::std::option::Option::Some(schema_fields)
+    }
+    fn field_default() -> ::std::option::Option<::serde_json::Value> {
+        ::std::option::Option::None
+    }
+}
+enum Basic {
+    A,
+    B,
+    C,
+    D,
+}
+#[automatically_derived]
+impl ::apache_avro::AvroSchemaComponent for Basic {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> ::apache_avro::schema::Schema {
+        let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
+                "Basic",
+                enclosing_namespace,
+            )
+            .expect("Unable to parse schema name Basic");
+        if named_schemas.contains(&name) {
+            ::apache_avro::schema::Schema::Ref {
+                name,
+            }
+        } else {
+            let enclosing_namespace = name.namespace();
+            named_schemas.insert(name.clone());
+            
::apache_avro::schema::Schema::Enum(::apache_avro::schema::EnumSchema {
+                name,
+                aliases: ::std::option::Option::None,
+                doc: ::std::option::Option::None,
+                symbols: ::alloc::boxed::box_assume_init_into_vec_unsafe(
+                    ::alloc::intrinsics::write_box_via_move(
+                        ::alloc::boxed::Box::new_uninit(),
+                        ["A".to_owned(), "B".to_owned(), "C".to_owned(), 
"D".to_owned()],
+                    ),
+                ),
+                default: ::std::option::Option::None,
+                attributes: ::std::collections::BTreeMap::new(),
+            })
+        }
+    }
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> 
::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
+        ::std::option::Option::None
+    }
+    fn field_default() -> ::std::option::Option<::serde_json::Value> {
+        ::std::option::Option::None
+    }
+}
diff --git a/avro_derive/tests/expanded/avro_rs_501_basic.rs 
b/avro_derive/tests/expanded/avro_rs_501_basic.rs
new file mode 100644
index 0000000..ea90024
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_rs_501_basic.rs
@@ -0,0 +1,32 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+struct A {
+    a: i32,
+    b: String,
+}
+
+#[derive(AvroSchema)]
+enum Basic {
+    A,
+    B,
+    C,
+    D,
+}
diff --git a/avro_derive/tests/expanded/avro_rs_501_namespace.expanded.rs 
b/avro_derive/tests/expanded/avro_rs_501_namespace.expanded.rs
new file mode 100644
index 0000000..ce84a5d
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_rs_501_namespace.expanded.rs
@@ -0,0 +1,133 @@
+use apache_avro::AvroSchema;
+#[avro(namespace = "namespace.testing")]
+struct A {
+    a: i32,
+    b: String,
+}
+#[automatically_derived]
+impl ::apache_avro::AvroSchemaComponent for A {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> ::apache_avro::schema::Schema {
+        let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
+                "namespace.testing.A",
+                enclosing_namespace,
+            )
+            .expect("Unable to parse schema name namespace.testing.A");
+        if named_schemas.contains(&name) {
+            ::apache_avro::schema::Schema::Ref {
+                name,
+            }
+        } else {
+            let enclosing_namespace = name.namespace();
+            named_schemas.insert(name.clone());
+            {
+                let mut schema_fields = ::std::vec::Vec::with_capacity(2usize);
+                schema_fields
+                    .push(::apache_avro::schema::RecordField {
+                        name: "a".to_string(),
+                        doc: ::std::option::Option::None,
+                        default: <i32 as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                        aliases: ::std::vec::Vec::new(),
+                        schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                            named_schemas,
+                            enclosing_namespace,
+                        ),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
+                    });
+                schema_fields
+                    .push(::apache_avro::schema::RecordField {
+                        name: "b".to_string(),
+                        doc: ::std::option::Option::None,
+                        default: <String as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                        aliases: ::std::vec::Vec::new(),
+                        schema: <String as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                            named_schemas,
+                            enclosing_namespace,
+                        ),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
+                    });
+                let schema_field_set: ::std::collections::HashSet<_> = 
schema_fields
+                    .iter()
+                    .map(|rf| &rf.name)
+                    .collect();
+                match (&schema_fields.len(), &schema_field_set.len()) {
+                    (left_val, right_val) => {
+                        if !(*left_val == *right_val) {
+                            let kind = ::core::panicking::AssertKind::Eq;
+                            ::core::panicking::assert_failed(
+                                kind,
+                                &*left_val,
+                                &*right_val,
+                                ::core::option::Option::Some(
+                                    format_args!(
+                                        "Duplicate field names found: {0:?}", 
schema_fields,
+                                    ),
+                                ),
+                            );
+                        }
+                    }
+                };
+                let name = 
::apache_avro::schema::Name::new("namespace.testing.A")
+                    .expect(
+                        &::alloc::__export::must_use({
+                            ::alloc::fmt::format(
+                                format_args!(
+                                    "Unable to parse struct name for schema 
{0}",
+                                    "namespace.testing.A",
+                                ),
+                            )
+                        })[..],
+                    );
+                let lookup: ::std::collections::BTreeMap<String, usize> = 
schema_fields
+                    .iter()
+                    .enumerate()
+                    .map(|(position, field)| (field.name.to_owned(), position))
+                    .collect();
+                
::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema {
+                    name,
+                    aliases: ::std::option::Option::None,
+                    doc: ::std::option::Option::None,
+                    fields: schema_fields,
+                    lookup,
+                    attributes: ::std::collections::BTreeMap::new(),
+                })
+            }
+        }
+    }
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> 
::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
+        let mut schema_fields = ::std::vec::Vec::with_capacity(2usize);
+        schema_fields
+            .push(::apache_avro::schema::RecordField {
+                name: "a".to_string(),
+                doc: ::std::option::Option::None,
+                default: <i32 as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                aliases: ::std::vec::Vec::new(),
+                schema: <i32 as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                    named_schemas,
+                    enclosing_namespace,
+                ),
+                custom_attributes: ::std::collections::BTreeMap::new(),
+            });
+        schema_fields
+            .push(::apache_avro::schema::RecordField {
+                name: "b".to_string(),
+                doc: ::std::option::Option::None,
+                default: <String as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                aliases: ::std::vec::Vec::new(),
+                schema: <String as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                    named_schemas,
+                    enclosing_namespace,
+                ),
+                custom_attributes: ::std::collections::BTreeMap::new(),
+            });
+        ::std::option::Option::Some(schema_fields)
+    }
+    fn field_default() -> ::std::option::Option<::serde_json::Value> {
+        ::std::option::Option::None
+    }
+}
diff --git a/avro_derive/tests/expanded/avro_rs_501_namespace.rs 
b/avro_derive/tests/expanded/avro_rs_501_namespace.rs
new file mode 100644
index 0000000..ea7c89f
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_rs_501_namespace.rs
@@ -0,0 +1,25 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+#[avro(namespace = "namespace.testing")]
+struct A {
+    a: i32,
+    b: String,
+}
diff --git a/avro_derive/tests/expanded/avro_rs_501_reference.expanded.rs 
b/avro_derive/tests/expanded/avro_rs_501_reference.expanded.rs
new file mode 100644
index 0000000..9916d4f
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_rs_501_reference.expanded.rs
@@ -0,0 +1,139 @@
+use apache_avro::AvroSchema;
+struct A<'a> {
+    a: &'a Vec<i32>,
+    b: &'static str,
+}
+#[automatically_derived]
+impl<'a> ::apache_avro::AvroSchemaComponent for A<'a> {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> ::apache_avro::schema::Schema {
+        let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
+                "A",
+                enclosing_namespace,
+            )
+            .expect("Unable to parse schema name A");
+        if named_schemas.contains(&name) {
+            ::apache_avro::schema::Schema::Ref {
+                name,
+            }
+        } else {
+            let enclosing_namespace = name.namespace();
+            named_schemas.insert(name.clone());
+            {
+                let mut schema_fields = ::std::vec::Vec::with_capacity(2usize);
+                schema_fields
+                    .push(::apache_avro::schema::RecordField {
+                        name: "a".to_string(),
+                        doc: ::std::option::Option::None,
+                        default: <&'a Vec<
+                            i32,
+                        > as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                        aliases: ::std::vec::Vec::new(),
+                        schema: <&'a Vec<
+                            i32,
+                        > as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                            named_schemas,
+                            enclosing_namespace,
+                        ),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
+                    });
+                schema_fields
+                    .push(::apache_avro::schema::RecordField {
+                        name: "b".to_string(),
+                        doc: ::std::option::Option::None,
+                        default: <&'static str as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                        aliases: ::std::vec::Vec::new(),
+                        schema: <&'static str as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                            named_schemas,
+                            enclosing_namespace,
+                        ),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
+                    });
+                let schema_field_set: ::std::collections::HashSet<_> = 
schema_fields
+                    .iter()
+                    .map(|rf| &rf.name)
+                    .collect();
+                match (&schema_fields.len(), &schema_field_set.len()) {
+                    (left_val, right_val) => {
+                        if !(*left_val == *right_val) {
+                            let kind = ::core::panicking::AssertKind::Eq;
+                            ::core::panicking::assert_failed(
+                                kind,
+                                &*left_val,
+                                &*right_val,
+                                ::core::option::Option::Some(
+                                    format_args!(
+                                        "Duplicate field names found: {0:?}", 
schema_fields,
+                                    ),
+                                ),
+                            );
+                        }
+                    }
+                };
+                let name = ::apache_avro::schema::Name::new("A")
+                    .expect(
+                        &::alloc::__export::must_use({
+                            ::alloc::fmt::format(
+                                format_args!(
+                                    "Unable to parse struct name for schema 
{0}", "A",
+                                ),
+                            )
+                        })[..],
+                    );
+                let lookup: ::std::collections::BTreeMap<String, usize> = 
schema_fields
+                    .iter()
+                    .enumerate()
+                    .map(|(position, field)| (field.name.to_owned(), position))
+                    .collect();
+                
::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema {
+                    name,
+                    aliases: ::std::option::Option::None,
+                    doc: ::std::option::Option::None,
+                    fields: schema_fields,
+                    lookup,
+                    attributes: ::std::collections::BTreeMap::new(),
+                })
+            }
+        }
+    }
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> 
::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
+        let mut schema_fields = ::std::vec::Vec::with_capacity(2usize);
+        schema_fields
+            .push(::apache_avro::schema::RecordField {
+                name: "a".to_string(),
+                doc: ::std::option::Option::None,
+                default: <&'a Vec<
+                    i32,
+                > as ::apache_avro::AvroSchemaComponent>::field_default(),
+                aliases: ::std::vec::Vec::new(),
+                schema: <&'a Vec<
+                    i32,
+                > as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                    named_schemas,
+                    enclosing_namespace,
+                ),
+                custom_attributes: ::std::collections::BTreeMap::new(),
+            });
+        schema_fields
+            .push(::apache_avro::schema::RecordField {
+                name: "b".to_string(),
+                doc: ::std::option::Option::None,
+                default: <&'static str as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                aliases: ::std::vec::Vec::new(),
+                schema: <&'static str as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                    named_schemas,
+                    enclosing_namespace,
+                ),
+                custom_attributes: ::std::collections::BTreeMap::new(),
+            });
+        ::std::option::Option::Some(schema_fields)
+    }
+    fn field_default() -> ::std::option::Option<::serde_json::Value> {
+        ::std::option::Option::None
+    }
+}
diff --git a/avro_derive/tests/expanded/avro_rs_501_reference.rs 
b/avro_derive/tests/expanded/avro_rs_501_reference.rs
new file mode 100644
index 0000000..af8b4e2
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_rs_501_reference.rs
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+struct A<'a> {
+    a: &'a Vec<i32>,
+    b: &'static str,
+}
diff --git 
a/avro_derive/tests/expanded/avro_rs_501_struct_with_optional.expanded.rs 
b/avro_derive/tests/expanded/avro_rs_501_struct_with_optional.expanded.rs
new file mode 100644
index 0000000..076143e
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_rs_501_struct_with_optional.expanded.rs
@@ -0,0 +1,115 @@
+use apache_avro::AvroSchema;
+struct TestOptional {
+    a: Option<i32>,
+}
+#[automatically_derived]
+impl ::apache_avro::AvroSchemaComponent for TestOptional {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> ::apache_avro::schema::Schema {
+        let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
+                "TestOptional",
+                enclosing_namespace,
+            )
+            .expect("Unable to parse schema name TestOptional");
+        if named_schemas.contains(&name) {
+            ::apache_avro::schema::Schema::Ref {
+                name,
+            }
+        } else {
+            let enclosing_namespace = name.namespace();
+            named_schemas.insert(name.clone());
+            {
+                let mut schema_fields = ::std::vec::Vec::with_capacity(1usize);
+                schema_fields
+                    .push(::apache_avro::schema::RecordField {
+                        name: "a".to_string(),
+                        doc: ::std::option::Option::None,
+                        default: <Option<
+                            i32,
+                        > as 
::apache_avro::AvroSchemaComponent>::field_default(),
+                        aliases: ::std::vec::Vec::new(),
+                        schema: <Option<
+                            i32,
+                        > as 
::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                            named_schemas,
+                            enclosing_namespace,
+                        ),
+                        custom_attributes: ::std::collections::BTreeMap::new(),
+                    });
+                let schema_field_set: ::std::collections::HashSet<_> = 
schema_fields
+                    .iter()
+                    .map(|rf| &rf.name)
+                    .collect();
+                match (&schema_fields.len(), &schema_field_set.len()) {
+                    (left_val, right_val) => {
+                        if !(*left_val == *right_val) {
+                            let kind = ::core::panicking::AssertKind::Eq;
+                            ::core::panicking::assert_failed(
+                                kind,
+                                &*left_val,
+                                &*right_val,
+                                ::core::option::Option::Some(
+                                    format_args!(
+                                        "Duplicate field names found: {0:?}", 
schema_fields,
+                                    ),
+                                ),
+                            );
+                        }
+                    }
+                };
+                let name = ::apache_avro::schema::Name::new("TestOptional")
+                    .expect(
+                        &::alloc::__export::must_use({
+                            ::alloc::fmt::format(
+                                format_args!(
+                                    "Unable to parse struct name for schema 
{0}",
+                                    "TestOptional",
+                                ),
+                            )
+                        })[..],
+                    );
+                let lookup: ::std::collections::BTreeMap<String, usize> = 
schema_fields
+                    .iter()
+                    .enumerate()
+                    .map(|(position, field)| (field.name.to_owned(), position))
+                    .collect();
+                
::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema {
+                    name,
+                    aliases: ::std::option::Option::None,
+                    doc: ::std::option::Option::None,
+                    fields: schema_fields,
+                    lookup,
+                    attributes: ::std::collections::BTreeMap::new(),
+                })
+            }
+        }
+    }
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut 
::std::collections::HashSet<::apache_avro::schema::Name>,
+        enclosing_namespace: ::apache_avro::schema::NamespaceRef,
+    ) -> 
::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
+        let mut schema_fields = ::std::vec::Vec::with_capacity(1usize);
+        schema_fields
+            .push(::apache_avro::schema::RecordField {
+                name: "a".to_string(),
+                doc: ::std::option::Option::None,
+                default: <Option<
+                    i32,
+                > as ::apache_avro::AvroSchemaComponent>::field_default(),
+                aliases: ::std::vec::Vec::new(),
+                schema: <Option<
+                    i32,
+                > as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+                    named_schemas,
+                    enclosing_namespace,
+                ),
+                custom_attributes: ::std::collections::BTreeMap::new(),
+            });
+        ::std::option::Option::Some(schema_fields)
+    }
+    fn field_default() -> ::std::option::Option<::serde_json::Value> {
+        ::std::option::Option::None
+    }
+}
diff --git a/avro_derive/tests/expanded/avro_rs_501_struct_with_optional.rs 
b/avro_derive/tests/expanded/avro_rs_501_struct_with_optional.rs
new file mode 100644
index 0000000..5b9a63f
--- /dev/null
+++ b/avro_derive/tests/expanded/avro_rs_501_struct_with_optional.rs
@@ -0,0 +1,23 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+struct TestOptional {
+    a: Option<i32>,
+}
diff --git a/avro_derive/tests/expanded/mod.rs 
b/avro_derive/tests/expanded/mod.rs
new file mode 100644
index 0000000..adaa76a
--- /dev/null
+++ b/avro_derive/tests/expanded/mod.rs
@@ -0,0 +1,29 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#![expect(dead_code, reason = "Only code generation is tested")]
+
+// Do not include the `.expanded` modules here, as that code won't compile
+
+mod avro_3687_basic_enum_with_default;
+mod avro_3709_record_field_attributes;
+mod avro_rs_207_rename_all_attribute;
+mod avro_rs_207_rename_attr_over_rename_all_attribute;
+mod avro_rs_501_basic;
+mod avro_rs_501_namespace;
+mod avro_rs_501_reference;
+mod avro_rs_501_struct_with_optional;
diff --git a/avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.rs 
b/avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.rs
new file mode 100644
index 0000000..2ba3e44
--- /dev/null
+++ b/avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.rs
@@ -0,0 +1,30 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema, Default)]
+enum Basic {
+    #[default]
+    A,
+    B,
+    #[default]
+    C,
+    D
+}
+
+fn main() {}
diff --git 
a/avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.stderr 
b/avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.stderr
new file mode 100644
index 0000000..14a3798
--- /dev/null
+++ b/avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.stderr
@@ -0,0 +1,19 @@
+error: Multiple defaults defined: ["A", "C"]
+  --> tests/ui/avro_3687_basic_enum_with_default_twice.rs:21:6
+   |
+21 | enum Basic {
+   |      ^^^^^
+
+error: multiple declared defaults
+  --> tests/ui/avro_3687_basic_enum_with_default_twice.rs:20:22
+   |
+20 | #[derive(AvroSchema, Default)]
+   |                      ^^^^^^^
+...
+23 |     A,
+   |     - first default
+...
+26 |     C,
+   |     - additional default
+   |
+   = note: only one variant can be default
diff --git a/avro_derive/tests/ui/avro_rs_501_non_basic_enum.rs 
b/avro_derive/tests/ui/avro_rs_501_non_basic_enum.rs
new file mode 100644
index 0000000..72230d8
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_501_non_basic_enum.rs
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+enum NonBasic {
+    A(i32),
+    B,
+    C,
+    D
+}
+
+fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_501_non_basic_enum.stderr 
b/avro_derive/tests/ui/avro_rs_501_non_basic_enum.stderr
new file mode 100644
index 0000000..961253b
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_501_non_basic_enum.stderr
@@ -0,0 +1,5 @@
+error: AvroSchema: derive does not work for enums with non unit structs
+  --> tests/ui/avro_rs_501_non_basic_enum.rs:21:6
+   |
+21 | enum NonBasic {
+   |      ^^^^^^^^
diff --git a/avro_derive/tests/ui/avro_rs_501_tuple_struct.rs 
b/avro_derive/tests/ui/avro_rs_501_tuple_struct.rs
new file mode 100644
index 0000000..1e64845
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_501_tuple_struct.rs
@@ -0,0 +1,23 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+struct B(i32, String);
+
+fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_501_tuple_struct.stderr 
b/avro_derive/tests/ui/avro_rs_501_tuple_struct.stderr
new file mode 100644
index 0000000..1091ec0
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_501_tuple_struct.stderr
@@ -0,0 +1,5 @@
+error: AvroSchema derive does not work for tuple structs
+  --> tests/ui/avro_rs_501_tuple_struct.rs:21:8
+   |
+21 | struct B(i32, String);
+   |        ^
diff --git a/avro_derive/tests/ui/avro_rs_501_unit_struct.rs 
b/avro_derive/tests/ui/avro_rs_501_unit_struct.rs
new file mode 100644
index 0000000..1b740af
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_501_unit_struct.rs
@@ -0,0 +1,23 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+struct AbsoluteUnit;
+
+fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_501_unit_struct.stderr 
b/avro_derive/tests/ui/avro_rs_501_unit_struct.stderr
new file mode 100644
index 0000000..2a16ef6
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_501_unit_struct.stderr
@@ -0,0 +1,5 @@
+error: AvroSchema derive does not work for unit structs
+  --> tests/ui/avro_rs_501_unit_struct.rs:21:8
+   |
+21 | struct AbsoluteUnit;
+   |        ^^^^^^^^^^^^
diff --git a/licenserc.toml b/licenserc.toml
index 2c153a7..7ef9fe6 100644
--- a/licenserc.toml
+++ b/licenserc.toml
@@ -18,6 +18,7 @@
 headerPath = "Apache-2.0-ASF.txt"
 excludes = [
   "*.stderr",
+  "*.expanded.rs",
   "*.avro",
   "NOTICE",
   "README.tpl",

Reply via email to