You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by kr...@apache.org on 2025/12/23 12:38:08 UTC

(avro-rs) 01/01: feat: Use the Serde attributes and check for conflicting attributes

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

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

commit fad24999eec034da45372285175cda715ac31e98
Author: default <ad...@kriskras99.nl>
AuthorDate: Thu Dec 18 14:25:07 2025 +0000

    feat: Use the Serde attributes and check for conflicting attributes
---
 Cargo.lock                                         |  107 +
 avro/tests/avro-rs-226.rs                          |   66 -
 avro_derive/Cargo.toml                             |    1 +
 avro_derive/src/attributes/avro.rs                 |   40 +
 avro_derive/src/attributes/mod.rs                  |  202 ++
 avro_derive/src/attributes/serde.rs                |  313 ++
 avro_derive/src/case.rs                            |   44 +-
 avro_derive/src/lib.rs                             |   78 +-
 avro_derive/tests/derive.rs                        | 3205 ++++++++++----------
 avro_derive/tests/serde.rs                         |  698 +++++
 avro_derive/tests/ui.rs                            |    5 +
 .../tests/ui/avro_rs_226_skip_serializing.rs       |   11 +
 .../tests/ui/avro_rs_226_skip_serializing.stderr   |    5 +
 .../tests/ui/avro_rs_226_skip_serializing_if.rs    |   11 +
 .../ui/avro_rs_226_skip_serializing_if.stderr      |    5 +
 avro_derive/tests/ui/avro_rs_xxx_remote.rs         |   16 +
 avro_derive/tests/ui/avro_rs_xxx_remote.stderr     |    5 +
 .../tests/ui/avro_rs_xxx_rename_all_fields.rs      |   12 +
 .../tests/ui/avro_rs_xxx_rename_all_fields.stderr  |    5 +
 .../tests/ui/avro_rs_xxx_skip_de_serializing.rs    |   11 +
 .../ui/avro_rs_xxx_skip_de_serializing.stderr      |    5 +
 avro_derive/tests/ui/avro_rs_xxx_tag struct.rs     |   10 +
 avro_derive/tests/ui/avro_rs_xxx_tag struct.stderr |    5 +
 .../tests/ui/avro_rs_xxx_tag_content_enum.rs       |   10 +
 .../tests/ui/avro_rs_xxx_tag_content_enum.stderr   |    5 +
 avro_derive/tests/ui/avro_rs_xxx_tag_enum.rs       |   10 +
 avro_derive/tests/ui/avro_rs_xxx_tag_enum.stderr   |    5 +
 avro_derive/tests/ui/avro_rs_xxx_transparent.rs    |   15 +
 .../tests/ui/avro_rs_xxx_transparent.stderr        |    5 +
 avro_derive/tests/ui/avro_rs_xxx_untagged_enum.rs  |   10 +
 .../tests/ui/avro_rs_xxx_untagged_enum.stderr      |    5 +
 31 files changed, 3176 insertions(+), 1749 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 588cf6b..3cb7a47 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -103,6 +103,7 @@ dependencies = [
  "serde",
  "serde_json",
  "syn",
+ "trybuild",
 ]
 
 [[package]]
@@ -505,6 +506,12 @@ dependencies = [
  "log",
 ]
 
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
 [[package]]
 name = "find-msvc-tools"
 version = "0.1.4"
@@ -562,6 +569,12 @@ dependencies = [
  "zerocopy",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+
 [[package]]
 name = "heck"
 version = "0.5.0"
@@ -591,6 +604,16 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
 
+[[package]]
+name = "indexmap"
+version = "2.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
 [[package]]
 name = "itertools"
 version = "0.13.0"
@@ -1061,6 +1084,15 @@ dependencies = [
  "serde_core",
 ]
 
+[[package]]
+name = "serde_spanned"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
+dependencies = [
+ "serde_core",
+]
+
 [[package]]
 name = "sha2"
 version = "0.10.9"
@@ -1119,6 +1151,21 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "target-triple"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b"
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
 [[package]]
 name = "thiserror"
 version = "2.0.17"
@@ -1149,6 +1196,60 @@ dependencies = [
  "serde_json",
 ]
 
+[[package]]
+name = "toml"
+version = "0.9.10+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48"
+dependencies = [
+ "indexmap",
+ "serde_core",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_parser",
+ "toml_writer",
+ "winnow",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.7.5+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.0.6+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
+dependencies = [
+ "winnow",
+]
+
+[[package]]
+name = "toml_writer"
+version = "1.0.6+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
+
+[[package]]
+name = "trybuild"
+version = "1.0.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e17e807bff86d2a06b52bca4276746584a78375055b6e45843925ce2802b335"
+dependencies = [
+ "glob",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "target-triple",
+ "termcolor",
+ "toml",
+]
+
 [[package]]
 name = "typenum"
 version = "1.19.0"
@@ -1422,6 +1523,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
+[[package]]
+name = "winnow"
+version = "0.7.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
+
 [[package]]
 name = "wit-bindgen"
 version = "0.46.0"
diff --git a/avro/tests/avro-rs-226.rs b/avro/tests/avro-rs-226.rs
index 15f060b..060fc9d 100644
--- a/avro/tests/avro-rs-226.rs
+++ b/avro/tests/avro-rs-226.rs
@@ -133,69 +133,3 @@ fn avro_rs_226_index_out_of_bounds_with_serde_skip_multiple_fields() -> TestResu
         },
     )
 }
-
-#[test]
-#[should_panic(expected = "Missing default for skipped field 'y' for schema")]
-fn avro_rs_351_no_default_for_serde_skip_serializing_if_should_panic() {
-    #[derive(AvroSchema, Clone, Debug, Deserialize, PartialEq, Serialize)]
-    struct T {
-        x: Option<i8>,
-        #[serde(skip_serializing_if = "Option::is_none")]
-        y: Option<String>,
-        z: Option<i8>,
-    }
-
-    ser_deser::<T>(
-        &T::get_schema(),
-        T {
-            x: None,
-            y: None,
-            z: Some(1),
-        },
-    )
-    .unwrap()
-}
-
-#[test]
-#[should_panic(expected = "Missing default for skipped field 'x' for schema")]
-fn avro_rs_351_no_default_for_serde_skip_should_panic() {
-    #[derive(AvroSchema, Clone, Debug, Deserialize, PartialEq, Serialize)]
-    struct T {
-        #[serde(skip)]
-        x: Option<i8>,
-        y: Option<String>,
-        z: Option<i8>,
-    }
-
-    ser_deser::<T>(
-        &T::get_schema(),
-        T {
-            x: None,
-            y: None,
-            z: Some(1),
-        },
-    )
-    .unwrap()
-}
-
-#[test]
-#[should_panic(expected = "Missing default for skipped field 'z' for schema")]
-fn avro_rs_351_no_default_for_serde_skip_serializing_should_panic() {
-    #[derive(AvroSchema, Clone, Debug, Deserialize, PartialEq, Serialize)]
-    struct T {
-        x: Option<i8>,
-        y: Option<String>,
-        #[serde(skip_serializing)]
-        z: Option<i8>,
-    }
-
-    ser_deser::<T>(
-        &T::get_schema(),
-        T {
-            x: Some(0),
-            y: None,
-            z: None,
-        },
-    )
-    .unwrap()
-}
diff --git a/avro_derive/Cargo.toml b/avro_derive/Cargo.toml
index 9b13bd3..793f419 100644
--- a/avro_derive/Cargo.toml
+++ b/avro_derive/Cargo.toml
@@ -43,6 +43,7 @@ apache-avro = { default-features = false, path = "../avro", features = ["derive"
 proptest = { default-features = false, version = "1.9.0", features = ["std"] }
 serde = { workspace = true }
 pretty_assertions = { workspace = true }
+trybuild = "1.0.114"
 
 [package.metadata.docs.rs]
 rustdoc-args = ["--cfg", "docsrs"]
diff --git a/avro_derive/src/attributes/avro.rs b/avro_derive/src/attributes/avro.rs
new file mode 100644
index 0000000..b478ec5
--- /dev/null
+++ b/avro_derive/src/attributes/avro.rs
@@ -0,0 +1,40 @@
+use crate::case::RenameRule;
+
+#[derive(darling::FromAttributes)]
+#[darling(attributes(avro))]
+pub struct FieldAttributes {
+    #[darling(default)]
+    pub doc: Option<String>,
+    #[darling(default)]
+    pub default: Option<String>,
+    #[darling(multiple)]
+    pub alias: Vec<String>,
+    #[darling(default)]
+    pub rename: Option<String>,
+    #[darling(default)]
+    pub skip: bool,
+    #[darling(default)]
+    pub flatten: bool,
+}
+
+#[derive(darling::FromAttributes)]
+#[darling(attributes(avro))]
+pub struct VariantAttributes {
+    #[darling(default)]
+    pub rename: Option<String>,
+}
+
+#[derive(darling::FromAttributes)]
+#[darling(attributes(avro))]
+pub struct ContainerAttributes {
+    #[darling(default)]
+    pub name: Option<String>,
+    #[darling(default)]
+    pub namespace: Option<String>,
+    #[darling(default)]
+    pub doc: Option<String>,
+    #[darling(multiple)]
+    pub alias: Vec<String>,
+    #[darling(default)]
+    pub rename_all: RenameRule,
+}
diff --git a/avro_derive/src/attributes/mod.rs b/avro_derive/src/attributes/mod.rs
new file mode 100644
index 0000000..2865d3e
--- /dev/null
+++ b/avro_derive/src/attributes/mod.rs
@@ -0,0 +1,202 @@
+use darling::FromAttributes;
+use proc_macro2::Span;
+use syn::Attribute;
+
+use crate::{case::RenameRule, darling_to_syn};
+
+pub mod avro;
+pub mod serde;
+
+#[derive(Default)]
+pub struct NamedTypeOptions {
+    pub name: Option<String>,
+    pub namespace: Option<String>,
+    pub doc: Option<String>,
+    pub alias: Vec<String>,
+    pub rename_all: RenameRule,
+}
+
+impl NamedTypeOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, Vec<syn::Error>> {
+        let avro =
+            avro::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde =
+            serde::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.tag.is_some()
+            || serde.content.is_some()
+            || serde.untagged
+            || serde.variant_identifier
+            || serde.field_identifier
+        {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support changing the tagging Serde generates (`tag`, `content`, `untagged`, `variant_identifier`, `field_identifier`)",
+            ));
+        }
+        if serde.remote.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `remote` attribute",
+            ));
+        }
+        if serde.transparent {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support Serde transparent",
+            ))
+        }
+        if serde.rename_all.deserialize != serde.rename_all.serialize {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support different rename rules for serializing and deserializing (`rename_all(serialize = \"..\", deserialize = \"..\")`)"
+            ))
+        }
+
+        // Check for conflicts between Serde and Avro
+        if avro.rename_all != RenameRule::None && serde.rename_all.serialize != avro.rename_all {
+            errors.push(syn::Error::new(
+                span,
+                "#[avro(rename_all = \"..\")] must match #[serde(rename_all = \"..\")]",
+            ))
+        }
+
+        if !errors.is_empty() {
+            return Err(errors);
+        }
+
+        Ok(Self {
+            name: avro.name,
+            namespace: avro.namespace,
+            doc: avro.doc,
+            alias: avro.alias,
+            rename_all: serde.rename_all.serialize,
+        })
+    }
+}
+
+pub struct FieldOptions {
+    pub doc: Option<String>,
+    pub default: Option<String>,
+    pub alias: Vec<String>,
+    pub rename: Option<String>,
+    pub skip: bool,
+    pub flatten: bool,
+}
+
+impl FieldOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, Vec<syn::Error>> {
+        let avro = avro::FieldAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde = serde::FieldAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.getter.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `getter` attribute",
+            ));
+        }
+
+        // Check for conflicts between Serde and Avro
+        if avro.skip && !serde.skip {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(skip)]` requires `#[serde(skip)]`, it's also deprecated. Please use only `#[serde(skip)]`"
+            ));
+        }
+        if avro.flatten && !serde.flatten {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(flatten)]` requires `#[serde(flatten)]`, it's also deprecated. Please use only `#[serde(flatten)]`"
+            ));
+        }
+        // TODO: rename and alias checking can be relaxed with a more complex check, would require the field name
+        if avro.rename.is_some() && serde.rename != avro.rename {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(rename = \"..\")]` must match `#[serde(rename = \"..\")]`, it's also deprecated. Please use only `#[serde(rename  = \"..\")]`"
+            ));
+        }
+        if serde.alias != avro.alias {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(alias = \"..\")]` must match `#[serde(alias = \"..\")]`, it's also deprecated. Please use only `#[serde(alias  = \"..\")]`"
+            ));
+        }
+        if serde.skip_serializing && serde.skip_deserializing {
+            errors.push(syn::Error::new(
+                span,
+                "Use `#[serde(skip)]` instead of `#[serde(skip_serializing, skip_deserializing)]`",
+            ));
+
+            // Don't want to suggest default for skip_serializing as it's not needed for skip
+            return Err(errors);
+        }
+        if ((serde.skip_serializing) || serde.skip_serializing_if.is_some())
+            && avro.default.is_none()
+        {
+            errors.push(syn::Error::new(
+                span,
+                "`#[serde(skip_serializing)]` and `#[serde(skip_serializing_if)]` require `#[avro(default = \"..\")]"
+            ));
+        }
+
+        if !errors.is_empty() {
+            return Err(errors);
+        }
+
+        Ok(Self {
+            doc: avro.doc,
+            default: avro.default,
+            alias: serde.alias,
+            rename: serde.rename,
+            skip: serde.skip,
+            flatten: serde.flatten,
+        })
+    }
+}
+
+pub struct VariantOptions {
+    pub rename: Option<String>,
+}
+
+impl VariantOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, Vec<syn::Error>> {
+        let avro = avro::VariantAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde =
+            serde::VariantAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.other || serde.untagged.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support changing the tagging Serde generates (`other`, `untagged`)",
+            ));
+        }
+
+        if avro.rename.is_some() && serde.rename != avro.rename {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(rename = \"..\")]` must match `#[serde(rename = \"..\")]`, it's also deprecated. Please use only `#[serde(rename  = \"..\")]`"
+            ));
+        }
+
+        if !errors.is_empty() {
+            return Err(errors);
+        }
+
+        Ok(Self {
+            rename: serde.rename,
+        })
+    }
+}
diff --git a/avro_derive/src/attributes/serde.rs b/avro_derive/src/attributes/serde.rs
new file mode 100644
index 0000000..0624ee6
--- /dev/null
+++ b/avro_derive/src/attributes/serde.rs
@@ -0,0 +1,313 @@
+//! Attribute parsing for Serde attributes
+//!
+//! # Serde attributes
+//! This module only parses the minimal amount to be able to read Serde attributes. This means
+//! most attributes are decoded as an `Option<String>` as the actual content is not relevant.
+//! Attributes which are not needed by the derive are prefixed with a `_` as we can't ignore them
+//! (this would be a compile error).
+//!
+//! If Serde adds new attributes they need to be added here too.
+
+use darling::{FromAttributes, FromMeta};
+use syn::Expr;
+
+use crate::case::RenameRule;
+
+// from_expr is used for `rename_all = ".."`
+// FromMeta is used for `rename_all(serialize = "..", ..)`
+#[derive(Debug, Default, FromMeta, PartialEq, Eq)]
+#[darling(from_expr = RenameAll::from_expr)]
+pub struct RenameAll {
+    #[darling(default)]
+    pub serialize: RenameRule,
+    #[darling(default)]
+    pub deserialize: RenameRule,
+}
+
+impl From<RenameRule> for RenameAll {
+    fn from(value: RenameRule) -> Self {
+        Self {
+            serialize: value,
+            deserialize: value,
+        }
+    }
+}
+
+impl RenameAll {
+    fn from_expr(expr: &Expr) -> darling::Result<Self> {
+        let Expr::Lit(lit) = expr else {
+            return Err(darling::Error::custom("Expected a string or a tuple!"));
+        };
+        let rule = RenameRule::from_value(&lit.lit)?;
+        Ok(RenameAll::from(rule))
+    }
+}
+
+#[derive(Debug, FromMeta, PartialEq)]
+#[darling(from_expr = |expr| Ok(SerdeDefault::Expr(expr.clone())))]
+pub enum SerdeDefault {
+    #[darling(word, skip)]
+    UseTrait,
+    Expr(Expr),
+}
+
+/// All relevant Serde attributes that a container can have.
+///
+/// This skips the following attributes:
+/// - `rename`: renames the container name, but it's not used in the (de)serialisation logic.
+/// - `rename_all_fields`: renames the fields of struct variants in an enum, but struct variants are not (yet) supported.
+/// - `deny_unknown_fields`: makes Serialize/Deserialize derives error on unkown fields, not relevant.
+/// - `bound`: type bound for Serialize/Deserialize derives, not relevent.
+/// - `from`/`try_from`/`into`: override the (de)serialize implementations. Can be problematic but impossible to know when.
+/// - `crate`: only relevant for macros messing with Serialize/Deserialize.
+/// - `expecting`: changes the error message, not relevant.
+#[derive(Debug, FromAttributes)]
+#[darling(attributes(serde))]
+pub struct ContainerAttributes {
+    #[darling(rename = "rename")]
+    /// Rename this container.
+    pub _rename: Option<String>,
+    /// Rename all the fields (if this is a struct) or variants (if this is an enum) according to the given case convention.
+    #[darling(default)]
+    pub rename_all: RenameAll,
+    /// Rename all the fields of the struct variants in this enum.
+    #[darling(default, rename = "rename_all_fields")]
+    pub _rename_all_fields: RenameAll,
+    /// Error when encountering unknown fields when deserialising.
+    #[darling(default, rename = "deny_unknown_fields")]
+    pub _deny_unknown_fields: bool,
+    /// Add/expect a tag during serialisation.
+    ///
+    /// When used on a struct, this adds an extra field which is not in the schema definition.
+    /// When used on an enum, serde transforms it into a struct which does not match the schema definition.
+    pub tag: Option<String>,
+    /// Put the content in a field with this name.
+    pub content: Option<String>,
+    /// This makes the enum transparent, (de)serializing based on the variant directly.
+    ///
+    /// This does not match the schema definition.
+    #[darling(default)]
+    pub untagged: bool,
+    /// Add a bound to the Serialize/Deserialize trait.
+    #[darling(default, rename = "bound")]
+    pub _bound: Option<String>,
+    /// When deserializing, any missing fields should be filled in from the struct's implementation of `Default`.
+    #[darling(rename = "default")]
+    pub _default: Option<SerdeDefault>,
+    /// This type is the serde implementation for a "remote" type.
+    ///
+    /// This makes the (de)serialisation use/return a different type.
+    pub remote: Option<String>,
+    /// Directly use the inner type for (de)serialisation.
+    #[darling(default)]
+    pub transparent: bool,
+    /// Deserialize using the given type and then convert to this type with `From`.
+    #[darling(default, rename = "from")]
+    pub _from: Option<String>,
+    /// Deserialize using the given type and then convert to this type with `TryFrom`.
+    #[darling(default, rename = "try_from")]
+    pub _try_from: Option<String>,
+    /// Convert this type to the given type using `Into` and then serialize using the given type.
+    #[darling(default, rename = "into")]
+    pub _into: Option<String>,
+    /// Use the Serde API at this path.
+    #[darling(default, rename = "crate")]
+    pub _crate: Option<String>,
+    /// Custom error text.
+    #[darling(default, rename = "expecting")]
+    pub _expecting: Option<String>,
+    /// This does something with tags, which are incompatible.
+    #[darling(default)]
+    pub variant_identifier: bool,
+    /// This does something with tags,  `` which are incompatible.
+    #[darling(default)]
+    pub field_identifier: bool,
+}
+
+/// All relevant Serde attributes that a enum variant can have.
+///
+/// This skips the following attributes:
+/// - `rename_all`: renames the the struct variant fields, but struct variants are not (yet) supported.
+/// - `serialize_with`/`deserialize_with`/`with`: override the (de)serialize implementations. Can be problematic but impossible to know when.
+/// - `borrow`: lifetime stuff, not relevant.
+/// - `bound`: type bound for Serialize/Deserialize derives, not relevent.
+#[derive(Debug, FromAttributes)]
+#[darling(attributes(serde))]
+pub struct VariantAttributes {
+    /// Rename the variant.
+    #[darling(default)]
+    pub rename: Option<String>,
+    /// Aliases for this variant, only used during deserialisation.
+    #[darling(multiple, rename = "alias")]
+    pub _alias: Vec<String>,
+    #[darling(default, rename = "rename_all")]
+    pub _rename_all: RenameAll,
+    /// Do not serialize or deserialize this variant.
+    #[darling(default, rename = "skip")]
+    pub _skip: bool,
+    /// Do not serialize this variant.
+    #[darling(default, rename = "skip_serializing")]
+    pub _skip_serializing: bool,
+    /// Do not deserialize this variant.
+    #[darling(default, rename = "skip_deserializing")]
+    pub _skip_deserializing: bool,
+    /// Use this function for serializing.
+    #[darling(rename = "serialize_with")]
+    pub _serialize_with: Option<String>,
+    /// Use this function for deserializing.
+    #[darling(rename = "deserialize_with")]
+    pub _deserialize_with: Option<String>,
+    /// Use this module for (de)serializing.
+    #[darling(rename = "with")]
+    pub _with: Option<String>,
+    /// Put trait bounds on the implementations.
+    #[darling(rename = "bound")]
+    pub _bound: Option<String>,
+    /// Put bounds on the lifetimes.
+    #[darling(rename = "borrow")]
+    pub _borrow: Option<SerdeBorrow>,
+    /// This does something with tags, which are incompatible.
+    #[darling(default)]
+    pub other: bool,
+    /// (De)serialize this variant as if it was not part of the enum.
+    pub untagged: Option<String>,
+}
+
+#[derive(Debug, FromMeta, PartialEq)]
+#[darling(from_expr = |expr| Ok(SerdeBorrow::Expr(expr.clone())))]
+pub enum SerdeBorrow {
+    #[darling(word, skip)]
+    Default,
+    Expr(Expr),
+}
+
+/// All relevant Serde attributes that a field can have.
+#[derive(Debug, FromAttributes)]
+#[darling(attributes(serde))]
+pub struct FieldAttributes {
+    /// Rename the field.
+    #[darling(default)]
+    pub rename: Option<String>,
+    /// Aliases for this field, only used during deserialisation.
+    #[darling(multiple)]
+    pub alias: Vec<String>,
+    /// When deserializing, if this field is missing use `Default` or the given function.
+    #[darling(rename = "default")]
+    pub _default: Option<SerdeDefault>,
+    #[darling(default)]
+    pub flatten: bool,
+    /// Do not serialize or deserialize this field.
+    #[darling(default)]
+    pub skip: bool,
+    /// Do not serialize this field.
+    #[darling(default)]
+    pub skip_serializing: bool,
+    /// Do not deserialize this field.
+    #[darling(default)]
+    pub skip_deserializing: bool,
+    /// Do not serialize this field if the function returns `false`.
+    pub skip_serializing_if: Option<String>,
+    /// Use this function for serializing.
+    #[darling(rename = "serialize_with")]
+    pub _serialize_with: Option<String>,
+    /// Use this function for deserializing.
+    #[darling(rename = "deserialize_with")]
+    pub _deserialize_with: Option<String>,
+    /// Use this module for (de)serializing.
+    #[darling(rename = "with")]
+    pub _with: Option<String>,
+    /// Put bounds on the lifetimes.
+    #[darling(rename = "borrow")]
+    pub _borrow: Option<SerdeBorrow>,
+    /// Used for remote types.
+    pub getter: Option<String>,
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{
+        RenameRule,
+        attributes::serde::{ContainerAttributes, RenameAll, SerdeDefault},
+    };
+    use darling::FromAttributes;
+    use syn::DeriveInput;
+
+    #[test]
+    fn test_rename_all() {
+        let derive: DeriveInput = syn::parse_quote! {
+            #[serde(rename_all = "lowercase")]
+            struct Config {
+                field: String,
+            }
+        };
+        let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
+        assert_eq!(input.rename_all, RenameAll::from(RenameRule::LowerCase));
+
+        let derive: DeriveInput = syn::parse_quote! {
+            #[serde(rename_all(serialize = "lowercase"))]
+            struct Config {
+                field: String,
+            }
+        };
+        let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
+        assert_eq!(
+            input.rename_all,
+            RenameAll {
+                serialize: RenameRule::LowerCase,
+                deserialize: RenameRule::None
+            }
+        );
+
+        let derive: DeriveInput = syn::parse_quote! {
+            #[serde(rename_all(deserialize = "lowercase"))]
+            struct Config {
+                field: String,
+            }
+        };
+        let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
+        assert_eq!(
+            input.rename_all,
+            RenameAll {
+                serialize: RenameRule::None,
+                deserialize: RenameRule::LowerCase
+            }
+        );
+
+        let derive: DeriveInput = syn::parse_quote! {
+            struct Config {
+                field: String,
+            }
+        };
+        let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
+        assert_eq!(input.rename_all, RenameAll::from(RenameRule::None));
+    }
+
+    #[test]
+    fn test_default() {
+        let derive: DeriveInput = syn::parse_quote! {
+            #[serde(default)]
+            struct Config {
+                field: String,
+            }
+        };
+        let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
+        assert_eq!(input._default, Some(SerdeDefault::UseTrait));
+
+        let derive: DeriveInput = syn::parse_quote! {
+            #[serde(default = "some::path")]
+            struct Config {
+                field: String,
+            }
+        };
+        let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
+        assert!(matches!(input._default, Some(SerdeDefault::Expr(_))));
+
+        let derive: DeriveInput = syn::parse_quote! {
+            struct Config {
+                field: String,
+            }
+        };
+        let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
+        assert_eq!(input._default, None);
+    }
+}
diff --git a/avro_derive/src/case.rs b/avro_derive/src/case.rs
index 8dbaafd..d0f3c8c 100644
--- a/avro_derive/src/case.rs
+++ b/avro_derive/src/case.rs
@@ -18,13 +18,17 @@
 //! Code to convert the Rust-styled field/variant (e.g. `my_field`, `MyType`) to the
 //! case of the source (e.g. `my-field`, `MY_FIELD`).
 //! Code copied from serde <https://github.com/serde-rs/serde/blob/master/serde_derive/src/internals/case.rs>
+use darling::FromMeta;
+use syn::Lit;
+
 use self::RenameRule::*;
 use std::fmt::{self, Debug, Display};
 
 /// The different possible ways to change case of fields in a struct, or variants in an enum.
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
 pub enum RenameRule {
     /// Don't apply a default rename rule.
+    #[default]
     None,
     /// Rename direct children to "lowercase" style.
     LowerCase,
@@ -47,6 +51,15 @@ pub enum RenameRule {
     ScreamingKebabCase,
 }
 
+impl FromMeta for RenameRule {
+    fn from_value(value: &Lit) -> darling::Result<Self> {
+        let Lit::Str(litstr) = value else {
+            panic!("Only expected a string");
+        };
+        Self::from_str(&litstr.value()).map_err(darling::Error::custom)
+    }
+}
+
 static RENAME_RULES: &[(&str, RenameRule)] = &[
     ("lowercase", LowerCase),
     ("UPPERCASE", UpperCase),
@@ -59,15 +72,20 @@ static RENAME_RULES: &[(&str, RenameRule)] = &[
 ];
 
 impl RenameRule {
-    pub fn from_str(rename_all_str: &str) -> Result<Self, ParseError<'_>> {
-        for (name, rule) in RENAME_RULES {
-            if rename_all_str == *name {
-                return Ok(*rule);
-            }
+    pub fn from_str(rename_all_str: &str) -> Result<Self, ParseError> {
+        match rename_all_str {
+            "lowercase" => Ok(Self::LowerCase),
+            "UPPERCASE" => Ok(Self::UpperCase),
+            "PascalCase" => Ok(Self::PascalCase),
+            "camelCase" => Ok(Self::CamelCase),
+            "snake_case" => Ok(Self::SnakeCase),
+            "SCREAMING_SNAKE_CASE" => Ok(Self::ScreamingSnakeCase),
+            "kebab-case" => Ok(Self::KebabCase),
+            "SCREAMING-KEBAB-CASE" => Ok(Self::ScreamingKebabCase),
+            _ => Err(ParseError {
+                unknown: rename_all_str.to_string(),
+            }),
         }
-        Err(ParseError {
-            unknown: rename_all_str,
-        })
     }
 
     /// Apply a renaming rule to an enum variant, returning the version expected in the source.
@@ -126,14 +144,14 @@ impl RenameRule {
     }
 }
 
-pub struct ParseError<'a> {
-    unknown: &'a str,
+pub struct ParseError {
+    unknown: String,
 }
 
-impl Display for ParseError<'_> {
+impl Display for ParseError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.write_str("unknown rename rule `rename_all = ")?;
-        Debug::fmt(self.unknown, f)?;
+        Debug::fmt(&self.unknown, f)?;
         f.write_str("`, expected one of ")?;
         for (i, (name, _rule)) in RENAME_RULES.iter().enumerate() {
             if i > 0 {
diff --git a/avro_derive/src/lib.rs b/avro_derive/src/lib.rs
index 2b225fb..a331bdc 100644
--- a/avro_derive/src/lib.rs
+++ b/avro_derive/src/lib.rs
@@ -15,57 +15,20 @@
 // specific language governing permissions and limitations
 // under the License.
 
+mod attributes;
 mod case;
-use case::RenameRule;
-use darling::FromAttributes;
+
 use proc_macro2::{Span, TokenStream};
 use quote::quote;
-
 use syn::{
     AttrStyle, Attribute, DeriveInput, Ident, Meta, Type, TypePath, parse_macro_input,
     spanned::Spanned,
 };
 
-#[derive(darling::FromAttributes)]
-#[darling(attributes(avro))]
-struct FieldOptions {
-    #[darling(default)]
-    doc: Option<String>,
-    #[darling(default)]
-    default: Option<String>,
-    #[darling(multiple)]
-    alias: Vec<String>,
-    #[darling(default)]
-    rename: Option<String>,
-    #[darling(default)]
-    skip: Option<bool>,
-    #[darling(default)]
-    flatten: Option<bool>,
-}
+use crate::attributes::{FieldOptions, NamedTypeOptions, VariantOptions};
+use crate::case::RenameRule;
 
-#[derive(darling::FromAttributes)]
-#[darling(attributes(avro))]
-struct VariantOptions {
-    #[darling(default)]
-    rename: Option<String>,
-}
-
-#[derive(darling::FromAttributes)]
-#[darling(attributes(avro))]
-struct NamedTypeOptions {
-    #[darling(default)]
-    name: Option<String>,
-    #[darling(default)]
-    namespace: Option<String>,
-    #[darling(default)]
-    doc: Option<String>,
-    #[darling(multiple)]
-    alias: Vec<String>,
-    #[darling(default)]
-    rename_all: Option<String>,
-}
-
-#[proc_macro_derive(AvroSchema, attributes(avro))]
+#[proc_macro_derive(AvroSchema, attributes(avro, serde))]
 // Templated from Serde
 pub fn proc_macro_derive_avro_schema(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
     let mut input = parse_macro_input!(input as DeriveInput);
@@ -75,10 +38,9 @@ pub fn proc_macro_derive_avro_schema(input: proc_macro::TokenStream) -> proc_mac
 }
 
 fn derive_avro_schema(input: &mut DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
-    let named_type_options =
-        NamedTypeOptions::from_attributes(&input.attrs[..]).map_err(darling_to_syn)?;
+    let named_type_options = NamedTypeOptions::new(&input.attrs, input.span())?;
 
-    let rename_all = parse_case(named_type_options.rename_all.as_deref(), input.span())?;
+    let rename_all = named_type_options.rename_all;
     let name = named_type_options.name.unwrap_or(input.ident.to_string());
 
     let full_schema_name = vec![named_type_options.namespace, Some(name)]
@@ -153,8 +115,7 @@ fn get_data_struct_schema_def(
                 if let Some(raw_name) = name.strip_prefix("r#") {
                     name = raw_name.to_string();
                 }
-                let field_attrs =
-                    FieldOptions::from_attributes(&field.attrs).map_err(darling_to_syn)?;
+                let field_attrs = FieldOptions::new(&field.attrs, field.span())?;
                 let doc =
                     preserve_optional(field_attrs.doc.or_else(|| extract_outer_doc(&field.attrs)));
                 match (field_attrs.rename, rename_all) {
@@ -166,9 +127,9 @@ fn get_data_struct_schema_def(
                     }
                     _ => {}
                 }
-                if Some(true) == field_attrs.skip {
+                if field_attrs.skip {
                     continue;
-                } else if Some(true) == field_attrs.flatten {
+                } else if field_attrs.flatten {
                     // Inline the fields of the child record at runtime, as we don't have access to
                     // the schema here.
                     let flatten_ty = &field.ty;
@@ -271,8 +232,7 @@ fn get_data_enum_schema_def(
         let default = preserve_optional(default_value);
         let mut symbols = Vec::new();
         for variant in &e.variants {
-            let field_attrs =
-                VariantOptions::from_attributes(&variant.attrs[..]).map_err(darling_to_syn)?;
+            let field_attrs = VariantOptions::new(&variant.attrs, variant.span())?;
             let name = match (field_attrs.rename, rename_all) {
                 (Some(rename), _) => rename,
                 (None, rename_all) if !matches!(rename_all, RenameRule::None) => {
@@ -430,16 +390,6 @@ fn darling_to_syn(e: darling::Error) -> Vec<syn::Error> {
     vec![syn::Error::new(token_errors.span(), msg)]
 }
 
-fn parse_case(case: Option<&str>, span: Span) -> Result<RenameRule, Vec<syn::Error>> {
-    match case {
-        None => Ok(RenameRule::None),
-        Some(case) => {
-            Ok(RenameRule::from_str(case)
-                .map_err(|e| vec![syn::Error::new(span, e.to_string())])?)
-        }
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -701,6 +651,7 @@ mod tests {
     fn test_avro_3709_record_field_attributes() {
         let test_struct = quote! {
             struct A {
+                #[serde(alias = "a1", alias = "a2", rename = "a3")]
                 #[avro(alias = "a1", alias = "a2", doc = "a doc", default = "123", rename = "a3")]
                 a: i32
             }
@@ -720,6 +671,7 @@ mod tests {
 
         let test_enum = quote! {
             enum A {
+                #[serde(rename = "A3")]
                 #[avro(rename = "A3")]
                 Item1,
             }
@@ -741,6 +693,7 @@ mod tests {
     #[test]
     fn test_avro_rs_207_rename_all_attribute() {
         let test_struct = quote! {
+            #[serde(rename_all="SCREAMING_SNAKE_CASE")]
             #[avro(rename_all="SCREAMING_SNAKE_CASE")]
             struct A {
                 item: i32,
@@ -761,6 +714,7 @@ mod tests {
         };
 
         let test_enum = quote! {
+            #[serde(rename_all="SCREAMING_SNAKE_CASE")]
             #[avro(rename_all="SCREAMING_SNAKE_CASE")]
             enum B {
                 Item,
@@ -784,9 +738,11 @@ mod tests {
     #[test]
     fn test_avro_rs_207_rename_attr_has_priority_over_rename_all_attribute() {
         let test_struct = quote! {
+            #[serde(rename_all="SCREAMING_SNAKE_CASE")]
             #[avro(rename_all="SCREAMING_SNAKE_CASE")]
             struct A {
                 item: i32,
+                #[serde(rename="DoubleItem")]
                 #[avro(rename="DoubleItem")]
                 double_item: i32
             }
diff --git a/avro_derive/tests/derive.rs b/avro_derive/tests/derive.rs
index 6972e9a..9b9c8a7 100644
--- a/avro_derive/tests/derive.rs
+++ b/avro_derive/tests/derive.rs
@@ -24,331 +24,491 @@ use proptest::prelude::*;
 use serde::{Deserialize, Serialize, de::DeserializeOwned};
 use std::collections::HashMap;
 
-#[cfg(test)]
-mod test_derive {
-    use apache_avro::schema::{Alias, EnumSchema, RecordSchema};
-    use std::{borrow::Cow, sync::Mutex};
-
-    use super::*;
-    use pretty_assertions::assert_eq;
-
-    /// Takes in a type that implements the right combination of traits and runs it through a Serde Cycle and asserts the result is the same
-    fn serde_assert<T>(obj: T)
-    where
-        T: std::fmt::Debug + Serialize + DeserializeOwned + AvroSchema + Clone + PartialEq,
-    {
-        assert_eq!(obj, serde(obj.clone()));
-    }
+use apache_avro::schema::{Alias, EnumSchema, RecordSchema};
+use pretty_assertions::assert_eq;
+use std::{borrow::Cow, sync::Mutex};
+
+/// Takes in a type that implements the right combination of traits and runs it through a Serde Cycle and asserts the result is the same
+fn serde_assert<T>(obj: T)
+where
+    T: std::fmt::Debug + Serialize + DeserializeOwned + AvroSchema + Clone + PartialEq,
+{
+    assert_eq!(obj, serde(obj.clone()));
+}
 
-    fn serde<T>(obj: T) -> T
-    where
-        T: Serialize + DeserializeOwned + AvroSchema,
-    {
-        de(ser(obj))
+fn serde<T>(obj: T) -> T
+where
+    T: Serialize + DeserializeOwned + AvroSchema,
+{
+    de(ser(obj))
+}
+
+fn ser<T>(obj: T) -> Vec<u8>
+where
+    T: Serialize + AvroSchema,
+{
+    let schema = T::get_schema();
+    let mut writer = Writer::new(&schema, Vec::new()).unwrap();
+    if let Err(e) = writer.append_ser(obj) {
+        panic!("{e:?}");
     }
+    writer.into_inner().unwrap()
+}
 
-    fn ser<T>(obj: T) -> Vec<u8>
-    where
-        T: Serialize + AvroSchema,
-    {
-        let schema = T::get_schema();
-        let mut writer = Writer::new(&schema, Vec::new()).unwrap();
-        if let Err(e) = writer.append_ser(obj) {
-            panic!("{e:?}");
+fn de<T>(encoded: Vec<u8>) -> T
+where
+    T: DeserializeOwned + AvroSchema,
+{
+    assert!(!encoded.is_empty());
+    let schema = T::get_schema();
+    let mut reader = Reader::with_schema(&schema, &encoded[..]).unwrap();
+    if let Some(res) = reader.next() {
+        match res {
+            Ok(value) => {
+                return from_value::<T>(&value).unwrap();
+            }
+            Err(e) => panic!("{e:?}"),
         }
-        writer.into_inner().unwrap()
     }
+    unreachable!()
+}
+
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+struct TestBasic {
+    a: i32,
+    b: String,
+}
 
-    fn de<T>(encoded: Vec<u8>) -> T
-    where
-        T: DeserializeOwned + AvroSchema,
+proptest! {
+#[test]
+fn test_smoke_test(a: i32, b: String) {
+    let schema = r#"
     {
-        assert!(!encoded.is_empty());
-        let schema = T::get_schema();
-        let mut reader = Reader::with_schema(&schema, &encoded[..]).unwrap();
-        if let Some(res) = reader.next() {
-            match res {
-                Ok(value) => {
-                    return from_value::<T>(&value).unwrap();
-                }
-                Err(e) => panic!("{e:?}"),
+        "type":"record",
+        "name":"TestBasic",
+        "fields":[
+            {
+                "name":"a",
+                "type":"int"
+            },
+            {
+                "name":"b",
+                "type":"string"
             }
-        }
-        unreachable!()
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestBasic::get_schema());
+    let test = TestBasic {
+        a,
+        b,
+    };
+    serde_assert(test);
+}}
+
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+#[avro(namespace = "com.testing.namespace")]
+struct TestBasicNamespace {
+    a: i32,
+    b: String,
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    struct TestBasic {
-        a: i32,
-        b: String,
+#[test]
+fn test_basic_namespace() {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"com.testing.namespace.TestBasicNamespace",
+        "fields":[
+            {
+                "name":"a",
+                "type":"int"
+            },
+            {
+                "name":"b",
+                "type":"string"
+            }
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestBasicNamespace::get_schema());
+    if let Schema::Record(RecordSchema { name, .. }) = TestBasicNamespace::get_schema() {
+        assert_eq!("com.testing.namespace".to_owned(), name.namespace.unwrap())
+    } else {
+        panic!("TestBasicNamespace schema must be a record schema")
+    }
+}
 
-    proptest! {
-    #[test]
-    fn test_smoke_test(a: i32, b: String) {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestBasic",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"int"
-                },
-                {
-                    "name":"b",
-                    "type":"string"
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+#[avro(namespace = "com.testing.complex.namespace")]
+struct TestComplexNamespace {
+    a: TestBasicNamespace,
+    b: String,
+}
+
+#[test]
+fn test_complex_namespace() {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"com.testing.complex.namespace.TestComplexNamespace",
+        "fields":[
+            {
+                "name":"a",
+                "type":{
+                    "type":"record",
+                    "name":"com.testing.namespace.TestBasicNamespace",
+                    "fields":[
+                        {
+                            "name":"a",
+                            "type":"int"
+                        },
+                        {
+                            "name":"b",
+                            "type":"string"
+                        }
+                    ]
                 }
-            ]
+            },
+            {
+                "name":"b",
+                "type":"string"
+            }
+        ]
+    }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestComplexNamespace::get_schema());
+    if let Schema::Record(RecordSchema { name, fields, .. }) = TestComplexNamespace::get_schema() {
+        assert_eq!(
+            "com.testing.complex.namespace".to_owned(),
+            name.namespace.unwrap()
+        );
+        let inner_schema = fields
+            .iter()
+            .filter(|field| field.name == "a")
+            .map(|field| &field.schema)
+            .next();
+        if let Some(Schema::Record(RecordSchema { name, .. })) = inner_schema {
+            assert_eq!(
+                "com.testing.namespace".to_owned(),
+                name.namespace.clone().unwrap()
+            )
+        } else {
+            panic!("Field 'a' must have a record schema")
         }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestBasic::get_schema());
-        let test = TestBasic {
-            a,
-            b,
-        };
-        serde_assert(test);
-    }}
+    } else {
+        panic!("TestComplexNamespace schema must be a record schema")
+    }
+}
 
+#[test]
+fn avro_rs_239_test_named_record() {
     #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    #[avro(namespace = "com.testing.namespace")]
-    struct TestBasicNamespace {
+    #[avro(name = "Other", namespace = "com.testing.namespace")]
+    struct TestNamedRecord {
         a: i32,
         b: String,
     }
 
-    #[test]
-    fn test_basic_namespace() {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"com.testing.namespace.TestBasicNamespace",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"int"
-                },
-                {
-                    "name":"b",
-                    "type":"string"
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestBasicNamespace::get_schema());
-        if let Schema::Record(RecordSchema { name, .. }) = TestBasicNamespace::get_schema() {
-            assert_eq!("com.testing.namespace".to_owned(), name.namespace.unwrap())
-        } else {
-            panic!("TestBasicNamespace schema must be a record schema")
-        }
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"com.testing.namespace.Other",
+        "fields":[
+            {
+                "name":"a",
+                "type":"int"
+            },
+            {
+                "name":"b",
+                "type":"string"
+            }
+        ]
+    }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestNamedRecord::get_schema());
+    if let Schema::Record(RecordSchema { name, .. }) = TestNamedRecord::get_schema() {
+        assert_eq!("Other", name.name.as_str());
+        assert_eq!(Some("com.testing.namespace"), name.namespace.as_deref())
+    } else {
+        panic!("TestNamedRecord schema must be a record schema")
     }
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    #[avro(namespace = "com.testing.complex.namespace")]
-    struct TestComplexNamespace {
-        a: TestBasicNamespace,
-        b: String,
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+struct TestAllSupportedBaseTypes {
+    //Basics test
+    a: bool,
+    b: i8,
+    c: i16,
+    d: i32,
+    e: u8,
+    f: u16,
+    g: i64,
+    h: f32,
+    i: f64,
+    j: String,
+}
+
+proptest! {
+#[test]
+fn test_basic_types(a: bool, b: i8, c: i16, d: i32, e: u8, f: u16, g: i64, h: f32, i: f64, j: String) {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestAllSupportedBaseTypes",
+        "fields":[
+            {
+                "name":"a",
+                "type": "boolean"
+            },
+            {
+                "name":"b",
+                "type":"int"
+            },
+            {
+                "name":"c",
+                "type":"int"
+            },
+            {
+                "name":"d",
+                "type":"int"
+            },
+            {
+                "name":"e",
+                "type":"int"
+            },
+            {
+                "name":"f",
+                "type":"int"
+            },
+            {
+                "name":"g",
+                "type":"long"
+            },
+            {
+                "name":"h",
+                "type":"float"
+            },
+            {
+                "name":"i",
+                "type":"double"
+            },
+            {
+                "name":"j",
+                "type":"string"
+            }
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestAllSupportedBaseTypes::get_schema());
+    let all_basic = TestAllSupportedBaseTypes {
+        a,
+        b,
+        c,
+        d,
+        e,
+        f,
+        g,
+        h,
+        i,
+        j,
+    };
+    serde_assert(all_basic);
+}}
+
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+struct TestNested {
+    a: i32,
+    b: TestAllSupportedBaseTypes,
+}
 
-    #[test]
-    fn test_complex_namespace() {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"com.testing.complex.namespace.TestComplexNamespace",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":{
-                        "type":"record",
-                        "name":"com.testing.namespace.TestBasicNamespace",
-                        "fields":[
-                            {
-                                "name":"a",
-                                "type":"int"
-                            },
-                            {
-                                "name":"b",
-                                "type":"string"
-                            }
-                        ]
-                    }
-                },
-                {
-                    "name":"b",
-                    "type":"string"
+proptest! {
+#[test]
+fn test_inner_struct(a: bool, b: i8, c: i16, d: i32, e: u8, f: u16, g: i64, h: f32, i: f64, j: String, aa: i32) {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestNested",
+        "fields":[
+            {
+                "name":"a",
+                "type":"int"
+            },
+            {
+                "name":"b",
+                "type":{
+                    "type":"record",
+                    "name":"TestAllSupportedBaseTypes",
+                    "fields":[
+                        {
+                            "name":"a",
+                            "type": "boolean"
+                        },
+                        {
+                            "name":"b",
+                            "type":"int"
+                        },
+                        {
+                            "name":"c",
+                            "type":"int"
+                        },
+                        {
+                            "name":"d",
+                            "type":"int"
+                        },
+                        {
+                            "name":"e",
+                            "type":"int"
+                        },
+                        {
+                            "name":"f",
+                            "type":"int"
+                        },
+                        {
+                            "name":"g",
+                            "type":"long"
+                        },
+                        {
+                            "name":"h",
+                            "type":"float"
+                        },
+                        {
+                            "name":"i",
+                            "type":"double"
+                        },
+                        {
+                            "name":"j",
+                            "type":"string"
+                        }
+                    ]
                 }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestComplexNamespace::get_schema());
-        if let Schema::Record(RecordSchema { name, fields, .. }) =
-            TestComplexNamespace::get_schema()
-        {
-            assert_eq!(
-                "com.testing.complex.namespace".to_owned(),
-                name.namespace.unwrap()
-            );
-            let inner_schema = fields
-                .iter()
-                .filter(|field| field.name == "a")
-                .map(|field| &field.schema)
-                .next();
-            if let Some(Schema::Record(RecordSchema { name, .. })) = inner_schema {
-                assert_eq!(
-                    "com.testing.namespace".to_owned(),
-                    name.namespace.clone().unwrap()
-                )
-            } else {
-                panic!("Field 'a' must have a record schema")
             }
-        } else {
-            panic!("TestComplexNamespace schema must be a record schema")
-        }
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestNested::get_schema());
+    let all_basic = TestAllSupportedBaseTypes {
+        a,
+        b,
+        c,
+        d,
+        e,
+        f,
+        g,
+        h,
+        i,
+        j,
+    };
+    let inner_struct = TestNested {
+        a: aa,
+        b: all_basic,
+    };
+    serde_assert(inner_struct);
+}}
+
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+struct TestOptional {
+    a: Option<i32>,
+}
 
-    #[test]
-    fn avro_rs_239_test_named_record() {
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-        #[avro(name = "Other", namespace = "com.testing.namespace")]
-        struct TestNamedRecord {
-            a: i32,
-            b: String,
-        }
-
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"com.testing.namespace.Other",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"int"
-                },
-                {
-                    "name":"b",
-                    "type":"string"
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestNamedRecord::get_schema());
-        if let Schema::Record(RecordSchema { name, .. }) = TestNamedRecord::get_schema() {
-            assert_eq!("Other", name.name.as_str());
-            assert_eq!(Some("com.testing.namespace"), name.namespace.as_deref())
-        } else {
-            panic!("TestNamedRecord schema must be a record schema")
-        }
+proptest! {
+#[test]
+fn test_optional_field_some(a: i32) {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestOptional",
+        "fields":[
+            {
+                "name":"a",
+                "type":["null","int"]
+            }
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestOptional::get_schema());
+    let optional_field = TestOptional { a: Some(a) };
+    serde_assert(optional_field);
+}}
+
+#[test]
+fn test_optional_field_none() {
+    let optional_field = TestOptional { a: None };
+    serde_assert(optional_field);
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-    struct TestAllSupportedBaseTypes {
-        //Basics test
-        a: bool,
-        b: i8,
-        c: i16,
-        d: i32,
-        e: u8,
-        f: u16,
-        g: i64,
-        h: f32,
-        i: f64,
-        j: String,
-    }
-
-    proptest! {
-    #[test]
-    fn test_basic_types(a: bool, b: i8, c: i16, d: i32, e: u8, f: u16, g: i64, h: f32, i: f64, j: String) {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestAllSupportedBaseTypes",
-            "fields":[
-                {
-                    "name":"a",
-                    "type": "boolean"
-                },
-                {
-                    "name":"b",
-                    "type":"int"
-                },
-                {
-                    "name":"c",
-                    "type":"int"
-                },
-                {
-                    "name":"d",
-                    "type":"int"
-                },
-                {
-                    "name":"e",
-                    "type":"int"
-                },
-                {
-                    "name":"f",
-                    "type":"int"
-                },
-                {
-                    "name":"g",
-                    "type":"long"
-                },
-                {
-                    "name":"h",
-                    "type":"float"
-                },
-                {
-                    "name":"i",
-                    "type":"double"
-                },
-                {
-                    "name":"j",
-                    "type":"string"
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestAllSupportedBaseTypes::get_schema());
-        let all_basic = TestAllSupportedBaseTypes {
-            a,
-            b,
-            c,
-            d,
-            e,
-            f,
-            g,
-            h,
-            i,
-            j,
-        };
-        serde_assert(all_basic);
-    }}
+/// Generic Containers
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+struct TestGeneric<T: AvroSchemaComponent> {
+    a: String,
+    b: Vec<T>,
+    c: HashMap<String, T>,
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-    struct TestNested {
-        a: i32,
-        b: TestAllSupportedBaseTypes,
-    }
-
-    proptest! {
-    #[test]
-    fn test_inner_struct(a: bool, b: i8, c: i16, d: i32, e: u8, f: u16, g: i64, h: f32, i: f64, j: String, aa: i32) {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestNested",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"int"
-                },
-                {
-                    "name":"b",
-                    "type":{
+proptest! {
+#[test]
+fn test_generic_container_1(a: String, b: Vec<i32>, c: HashMap<String, i32>) {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestGeneric",
+        "fields":[
+            {
+                "name":"a",
+                "type":"string"
+            },
+            {
+                "name":"b",
+                "type": {
+                    "type":"array",
+                    "items":"int"
+                }
+            },
+            {
+                "name":"c",
+                "type": {
+                    "type":"map",
+                    "values":"int"
+                }
+            }
+        ]
+    }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestGeneric::<i32>::get_schema());
+    let test_generic = TestGeneric::<i32> {
+        a,
+        b,
+        c,
+    };
+    serde_assert(test_generic);
+}}
+
+proptest! {
+#[test]
+fn test_generic_container_2(a: bool, b: i8, c: i16, d: i32, e: u8, f: u16, g: i64, h: f32, i: f64, j: String) {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestGeneric",
+        "fields":[
+            {
+                "name":"a",
+                "type":"string"
+            },
+            {
+                "name":"b",
+                "type": {
+                    "type":"array",
+                    "items":{
                         "type":"record",
                         "name":"TestAllSupportedBaseTypes",
                         "fields":[
@@ -395,12 +555,25 @@ mod test_derive {
                         ]
                     }
                 }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestNested::get_schema());
-        let all_basic = TestAllSupportedBaseTypes {
+            },
+            {
+                "name":"c",
+                "type": {
+                    "type":"map",
+                    "values":"TestAllSupportedBaseTypes"
+                }
+            }
+        ]
+    }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(
+        schema,
+        TestGeneric::<TestAllSupportedBaseTypes>::get_schema()
+    );
+    let test_generic = TestGeneric::<TestAllSupportedBaseTypes> {
+        a: "testing".to_owned(),
+        b: vec![TestAllSupportedBaseTypes {
             a,
             b,
             c,
@@ -410,176 +583,11 @@ mod test_derive {
             g,
             h,
             i,
-            j,
-        };
-        let inner_struct = TestNested {
-            a: aa,
-            b: all_basic,
-        };
-        serde_assert(inner_struct);
-    }}
-
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    struct TestOptional {
-        a: Option<i32>,
-    }
-
-    proptest! {
-    #[test]
-    fn test_optional_field_some(a: i32) {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestOptional",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":["null","int"]
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestOptional::get_schema());
-        let optional_field = TestOptional { a: Some(a) };
-        serde_assert(optional_field);
-    }}
-
-    #[test]
-    fn test_optional_field_none() {
-        let optional_field = TestOptional { a: None };
-        serde_assert(optional_field);
-    }
-
-    /// Generic Containers
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-    struct TestGeneric<T: AvroSchemaComponent> {
-        a: String,
-        b: Vec<T>,
-        c: HashMap<String, T>,
-    }
-
-    proptest! {
-    #[test]
-    fn test_generic_container_1(a: String, b: Vec<i32>, c: HashMap<String, i32>) {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestGeneric",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"string"
-                },
-                {
-                    "name":"b",
-                    "type": {
-                        "type":"array",
-                        "items":"int"
-                    }
-                },
-                {
-                    "name":"c",
-                    "type": {
-                        "type":"map",
-                        "values":"int"
-                    }
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestGeneric::<i32>::get_schema());
-        let test_generic = TestGeneric::<i32> {
-            a,
-            b,
-            c,
-        };
-        serde_assert(test_generic);
-    }}
-
-    proptest! {
-    #[test]
-    fn test_generic_container_2(a: bool, b: i8, c: i16, d: i32, e: u8, f: u16, g: i64, h: f32, i: f64, j: String) {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestGeneric",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"string"
-                },
-                {
-                    "name":"b",
-                    "type": {
-                        "type":"array",
-                        "items":{
-                            "type":"record",
-                            "name":"TestAllSupportedBaseTypes",
-                            "fields":[
-                                {
-                                    "name":"a",
-                                    "type": "boolean"
-                                },
-                                {
-                                    "name":"b",
-                                    "type":"int"
-                                },
-                                {
-                                    "name":"c",
-                                    "type":"int"
-                                },
-                                {
-                                    "name":"d",
-                                    "type":"int"
-                                },
-                                {
-                                    "name":"e",
-                                    "type":"int"
-                                },
-                                {
-                                    "name":"f",
-                                    "type":"int"
-                                },
-                                {
-                                    "name":"g",
-                                    "type":"long"
-                                },
-                                {
-                                    "name":"h",
-                                    "type":"float"
-                                },
-                                {
-                                    "name":"i",
-                                    "type":"double"
-                                },
-                                {
-                                    "name":"j",
-                                    "type":"string"
-                                }
-                            ]
-                        }
-                    }
-                },
-                {
-                    "name":"c",
-                    "type": {
-                        "type":"map",
-                        "values":"TestAllSupportedBaseTypes"
-                    }
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(
-            schema,
-            TestGeneric::<TestAllSupportedBaseTypes>::get_schema()
-        );
-        let test_generic = TestGeneric::<TestAllSupportedBaseTypes> {
-            a: "testing".to_owned(),
-            b: vec![TestAllSupportedBaseTypes {
+            j: j.clone(),
+        }],
+        c: vec![(
+            "key".to_owned(),
+            TestAllSupportedBaseTypes {
                 a,
                 b,
                 c,
@@ -589,185 +597,259 @@ mod test_derive {
                 g,
                 h,
                 i,
-                j: j.clone(),
-            }],
-            c: vec![(
-                "key".to_owned(),
-                TestAllSupportedBaseTypes {
-                    a,
-                    b,
-                    c,
-                    d,
-                    e,
-                    f,
-                    g,
-                    h,
-                    i,
-                    j,
-                },
-            )]
-            .into_iter()
-            .collect(),
-        };
-        serde_assert(test_generic);
-    }}
+                j,
+            },
+        )]
+        .into_iter()
+        .collect(),
+    };
+    serde_assert(test_generic);
+}}
+
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+enum TestAllowedEnum {
+    A,
+    B,
+    C,
+    D,
+}
+
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+struct TestAllowedEnumNested {
+    a: TestAllowedEnum,
+    b: String,
+}
+
+#[test]
+fn test_enum() {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestAllowedEnumNested",
+        "fields":[
+            {
+                "name":"a",
+                "type": {
+                    "type":"enum",
+                    "name":"TestAllowedEnum",
+                    "symbols":["A","B","C","D"]
+                }
+            },
+            {
+                "name":"b",
+                "type":"string"
+            }
+        ]
+    }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestAllowedEnumNested::get_schema());
+    let enum_included = TestAllowedEnumNested {
+        a: TestAllowedEnum::B,
+        b: "hey".to_owned(),
+    };
+    serde_assert(enum_included);
+}
 
+#[test]
+fn avro_rs_239_test_enum_named() {
     #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    enum TestAllowedEnum {
+    #[avro(name = "Other", rename_all = "snake_case")]
+    #[serde(rename_all = "snake_case")]
+    enum TestNamedEnum {
         A,
         B,
         C,
+        #[avro(rename = "e")]
+        #[serde(rename = "e")]
         D,
     }
 
     #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    struct TestAllowedEnumNested {
-        a: TestAllowedEnum,
+    struct TestNamedEnumNested {
+        a: TestNamedEnum,
         b: String,
     }
 
-    #[test]
-    fn test_enum() {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestAllowedEnumNested",
-            "fields":[
-                {
-                    "name":"a",
-                    "type": {
-                        "type":"enum",
-                        "name":"TestAllowedEnum",
-                        "symbols":["A","B","C","D"]
-                    }
-                },
-                {
-                    "name":"b",
-                    "type":"string"
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestNamedEnumNested",
+        "fields":[
+            {
+                "name":"a",
+                "type": {
+                    "type":"enum",
+                    "name":"Other",
+                    "symbols":["a","b","c","e"]
                 }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestAllowedEnumNested::get_schema());
-        let enum_included = TestAllowedEnumNested {
-            a: TestAllowedEnum::B,
-            b: "hey".to_owned(),
-        };
-        serde_assert(enum_included);
-    }
-
-    #[test]
-    fn avro_rs_239_test_enum_named() {
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-        #[avro(name = "Other", rename_all = "snake_case")]
-        #[serde(rename_all = "snake_case")]
-        enum TestNamedEnum {
-            A,
-            B,
-            C,
-            #[avro(rename = "e")]
-            #[serde(rename = "e")]
-            D,
-        }
+            },
+            {
+                "name":"b",
+                "type":"string"
+            }
+        ]
+    }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestNamedEnumNested::get_schema());
+    let enum_included = TestNamedEnumNested {
+        a: TestNamedEnum::B,
+        b: "hey".to_owned(),
+    };
+    serde_assert(enum_included);
+}
 
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-        struct TestNamedEnumNested {
-            a: TestNamedEnum,
-            b: String,
-        }
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+struct ConsList {
+    value: i32,
+    next: Option<Box<ConsList>>,
+}
 
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestNamedEnumNested",
-            "fields":[
-                {
-                    "name":"a",
-                    "type": {
-                        "type":"enum",
-                        "name":"Other",
-                        "symbols":["a","b","c","e"]
-                    }
-                },
-                {
-                    "name":"b",
-                    "type":"string"
+#[test]
+fn test_cons() {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"ConsList",
+        "fields":[
+            {
+                "name":"value",
+                "type":"int"
+            },
+            {
+                "name":"next",
+                "type":["null","ConsList"]
+            }
+        ]
+    }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, ConsList::get_schema());
+    let list = ConsList {
+        value: 34,
+        next: Some(Box::new(ConsList {
+            value: 42,
+            next: None,
+        })),
+    };
+    serde_assert(list)
+}
+
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+struct ConsListGeneric<T: AvroSchemaComponent> {
+    value: T,
+    next: Option<Box<ConsListGeneric<T>>>,
+}
+
+#[test]
+fn test_cons_generic() {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"ConsListGeneric",
+        "fields":[
+            {
+                "name":"value",
+                "type":{
+                    "type":"record",
+                    "name":"TestAllowedEnumNested",
+                    "fields":[
+                        {
+                            "name":"a",
+                            "type": {
+                                "type":"enum",
+                                "name":"TestAllowedEnum",
+                                "symbols":["A","B","C","D"]
+                            }
+                        },
+                        {
+                            "name":"b",
+                            "type":"string"
+                        }
+                    ]
                 }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestNamedEnumNested::get_schema());
-        let enum_included = TestNamedEnumNested {
-            a: TestNamedEnum::B,
-            b: "hey".to_owned(),
-        };
-        serde_assert(enum_included);
+            },
+            {
+                "name":"next",
+                "type":["null","ConsListGeneric"]
+            }
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(
+        schema,
+        ConsListGeneric::<TestAllowedEnumNested>::get_schema()
+    );
+    let list = ConsListGeneric::<TestAllowedEnumNested> {
+        value: TestAllowedEnumNested {
+            a: TestAllowedEnum::B,
+            b: "testing".into(),
+        },
+        next: Some(Box::new(ConsListGeneric::<TestAllowedEnumNested> {
+            value: TestAllowedEnumNested {
+                a: TestAllowedEnum::D,
+                b: "testing2".into(),
+            },
+            next: None,
+        })),
+    };
+    serde_assert(list)
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-    struct ConsList {
-        value: i32,
-        next: Option<Box<ConsList>>,
-    }
-
-    #[test]
-    fn test_cons() {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"ConsList",
-            "fields":[
-                {
-                    "name":"value",
-                    "type":"int"
-                },
-                {
-                    "name":"next",
-                    "type":["null","ConsList"]
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+struct TestSimpleArray {
+    a: [i32; 4],
+}
+
+proptest! {
+#[test]
+fn test_simple_array(a: [i32; 4]) {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestSimpleArray",
+        "fields":[
+            {
+                "name":"a",
+                "type": {
+                    "type":"array",
+                    "items":"int"
                 }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, ConsList::get_schema());
-        let list = ConsList {
-            value: 34,
-            next: Some(Box::new(ConsList {
-                value: 42,
-                next: None,
-            })),
-        };
-        serde_assert(list)
+            }
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestSimpleArray::get_schema());
+    let test = TestSimpleArray { a };
+    serde_assert(test)
+}}
+
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+struct TestComplexArray<T: AvroSchemaComponent> {
+    a: [T; 2],
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-    struct ConsListGeneric<T: AvroSchemaComponent> {
-        value: T,
-        next: Option<Box<ConsListGeneric<T>>>,
-    }
-
-    #[test]
-    fn test_cons_generic() {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"ConsListGeneric",
-            "fields":[
-                {
-                    "name":"value",
-                    "type":{
+#[test]
+fn test_complex_array() {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestComplexArray",
+        "fields":[
+            {
+                "name":"a",
+                "type": {
+                    "type":"array",
+                    "items":{
                         "type":"record",
-                        "name":"TestAllowedEnumNested",
+                        "name":"TestBasic",
                         "fields":[
                             {
                                 "name":"a",
-                                "type": {
-                                    "type":"enum",
-                                    "name":"TestAllowedEnum",
-                                    "symbols":["A","B","C","D"]
-                                }
+                                "type":"int"
                             },
                             {
                                 "name":"b",
@@ -775,1079 +857,984 @@ mod test_derive {
                             }
                         ]
                     }
-                },
-                {
-                    "name":"next",
-                    "type":["null","ConsListGeneric"]
                 }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(
-            schema,
-            ConsListGeneric::<TestAllowedEnumNested>::get_schema()
-        );
-        let list = ConsListGeneric::<TestAllowedEnumNested> {
-            value: TestAllowedEnumNested {
-                a: TestAllowedEnum::B,
-                b: "testing".into(),
-            },
-            next: Some(Box::new(ConsListGeneric::<TestAllowedEnumNested> {
-                value: TestAllowedEnumNested {
-                    a: TestAllowedEnum::D,
-                    b: "testing2".into(),
-                },
-                next: None,
-            })),
-        };
-        serde_assert(list)
+            }
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestComplexArray::<TestBasic>::get_schema());
+    let test = TestComplexArray::<TestBasic> {
+        a: [
+            TestBasic {
+                a: 27,
+                b: "foo".to_owned(),
+            },
+            TestBasic {
+                a: 28,
+                b: "bar".to_owned(),
+            },
+        ],
+    };
+    serde_assert(test)
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    struct TestSimpleArray {
-        a: [i32; 4],
-    }
-
-    proptest! {
-    #[test]
-    fn test_simple_array(a: [i32; 4]) {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestSimpleArray",
-            "fields":[
-                {
-                    "name":"a",
-                    "type": {
-                        "type":"array",
-                        "items":"int"
-                    }
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestSimpleArray::get_schema());
-        let test = TestSimpleArray { a };
-        serde_assert(test)
-    }}
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+struct Testu8 {
+    a: Vec<u8>,
+    b: [u8; 2],
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-    struct TestComplexArray<T: AvroSchemaComponent> {
-        a: [T; 2],
-    }
-
-    #[test]
-    fn test_complex_array() {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestComplexArray",
-            "fields":[
-                {
-                    "name":"a",
-                    "type": {
-                        "type":"array",
-                        "items":{
-                            "type":"record",
-                            "name":"TestBasic",
-                            "fields":[
-                                {
-                                    "name":"a",
-                                    "type":"int"
-                                },
-                                {
-                                    "name":"b",
-                                    "type":"string"
-                                }
-                            ]
-                        }
-                    }
+proptest! {
+#[test]
+fn test_bytes_handled(a: Vec<u8>, b: [u8; 2]) {
+    let test = Testu8 {
+        a,
+        b,
+    };
+    serde_assert(test)
+    // don't check for schema equality to allow for transitioning to bytes or fixed types in the future
+}}
+
+#[derive(Debug, Serialize, Deserialize, AvroSchema)]
+struct TestSmartPointers<'a> {
+    a: String,
+    b: Mutex<Vec<i64>>,
+    c: Cow<'a, i32>,
+}
+
+#[test]
+fn test_smart_pointers() {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestSmartPointers",
+        "fields":[
+            {
+                "name":"a",
+                "type": "string"
+            },
+            {
+                "name":"b",
+                "type":{
+                    "type":"array",
+                    "items":"long"
                 }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestComplexArray::<TestBasic>::get_schema());
-        let test = TestComplexArray::<TestBasic> {
-            a: [
-                TestBasic {
-                    a: 27,
-                    b: "foo".to_owned(),
-                },
-                TestBasic {
-                    a: 28,
-                    b: "bar".to_owned(),
-                },
-            ],
-        };
-        serde_assert(test)
+            },
+            {
+                "name":"c",
+                "type":"int"
+            }
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestSmartPointers::get_schema());
+    let test = TestSmartPointers {
+        a: "hey".into(),
+        b: Mutex::new(vec![42]),
+        c: Cow::Owned(32),
+    };
+    // test serde with manual equality for mutex
+    let test = serde(test);
+    assert_eq!("hey", test.a);
+    assert_eq!(vec![42], *test.b.lock().unwrap());
+    assert_eq!(Cow::Owned::<i32>(32), test.c);
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    struct Testu8 {
-        a: Vec<u8>,
-        b: [u8; 2],
-    }
+#[derive(Debug, Serialize, AvroSchema, Clone, PartialEq)]
+struct TestReference<'a> {
+    a: &'a Vec<i32>,
+    b: &'static str,
+    c: &'a f64,
+}
 
-    proptest! {
-    #[test]
-    fn test_bytes_handled(a: Vec<u8>, b: [u8; 2]) {
-        let test = Testu8 {
-            a,
-            b,
-        };
-        serde_assert(test)
-        // don't check for schema equality to allow for transitioning to bytes or fixed types in the future
-    }}
-
-    #[derive(Debug, Serialize, Deserialize, AvroSchema)]
-    struct TestSmartPointers<'a> {
-        a: String,
-        b: Mutex<Vec<i64>>,
-        c: Cow<'a, i32>,
-    }
-
-    #[test]
-    fn test_smart_pointers() {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestSmartPointers",
-            "fields":[
-                {
-                    "name":"a",
-                    "type": "string"
-                },
-                {
-                    "name":"b",
-                    "type":{
-                        "type":"array",
-                        "items":"long"
-                    }
-                },
-                {
-                    "name":"c",
-                    "type":"int"
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestSmartPointers::get_schema());
-        let test = TestSmartPointers {
-            a: "hey".into(),
-            b: Mutex::new(vec![42]),
-            c: Cow::Owned(32),
-        };
-        // test serde with manual equality for mutex
-        let test = serde(test);
-        assert_eq!("hey", test.a);
-        assert_eq!(vec![42], *test.b.lock().unwrap());
-        assert_eq!(Cow::Owned::<i32>(32), test.c);
-    }
-
-    #[derive(Debug, Serialize, AvroSchema, Clone, PartialEq)]
-    struct TestReference<'a> {
-        a: &'a Vec<i32>,
-        b: &'static str,
-        c: &'a f64,
-    }
-
-    proptest! {
-    #[test]
-    fn test_reference_struct(a: Vec<i32>, c: f64) {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestReference",
-            "fields":[
-                {
-                    "name":"a",
-                    "type": {
-                        "type":"array",
-                        "items":"int"
-                    }
-                },
-                {
-                    "name":"b",
-                    "type":"string"
-                },
-                {
-                    "name":"c",
-                    "type":"double"
+proptest! {
+#[test]
+fn test_reference_struct(a: Vec<i32>, c: f64) {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestReference",
+        "fields":[
+            {
+                "name":"a",
+                "type": {
+                    "type":"array",
+                    "items":"int"
                 }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, TestReference::get_schema());
-        // let a = vec![34];
-        // let c = 4.55555555_f64;
-        let test = TestReference {
-            a: &a,
-            b: "testing_static",
-            c: &c,
-        };
-        ser(test);
-    }}
-
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    #[avro(namespace = "com.testing.namespace", doc = "A Documented Record")]
-    struct TestBasicWithAttributes {
-        #[avro(doc = "Milliseconds since Queen released Bohemian Rhapsody")]
-        a: i32,
-        #[avro(doc = "Full lyrics of Bohemian Rhapsody")]
-        b: String,
+            },
+            {
+                "name":"b",
+                "type":"string"
+            },
+            {
+                "name":"c",
+                "type":"double"
+            }
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, TestReference::get_schema());
+    // let a = vec![34];
+    // let c = 4.55555555_f64;
+    let test = TestReference {
+        a: &a,
+        b: "testing_static",
+        c: &c,
+    };
+    ser(test);
+}}
+
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+#[avro(namespace = "com.testing.namespace", doc = "A Documented Record")]
+struct TestBasicWithAttributes {
+    #[avro(doc = "Milliseconds since Queen released Bohemian Rhapsody")]
+    a: i32,
+    #[avro(doc = "Full lyrics of Bohemian Rhapsody")]
+    b: String,
+}
 
-    #[test]
-    fn test_basic_with_attributes() {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"com.testing.namespace.TestBasicWithAttributes",
-            "doc":"A Documented Record",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"int",
-                    "doc":"Milliseconds since Queen released Bohemian Rhapsody"
-                },
-                {
-                    "name":"b",
-                    "type": "string",
-                    "doc": "Full lyrics of Bohemian Rhapsody"
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        if let Schema::Record(RecordSchema { name, doc, .. }) =
-            TestBasicWithAttributes::get_schema()
-        {
-            assert_eq!("com.testing.namespace".to_owned(), name.namespace.unwrap());
-            assert_eq!("A Documented Record", doc.unwrap())
-        } else {
-            panic!("TestBasicWithAttributes schema must be a record schema")
-        }
-        assert_eq!(schema, TestBasicWithAttributes::get_schema());
+#[test]
+fn test_basic_with_attributes() {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"com.testing.namespace.TestBasicWithAttributes",
+        "doc":"A Documented Record",
+        "fields":[
+            {
+                "name":"a",
+                "type":"int",
+                "doc":"Milliseconds since Queen released Bohemian Rhapsody"
+            },
+            {
+                "name":"b",
+                "type": "string",
+                "doc": "Full lyrics of Bohemian Rhapsody"
+            }
+        ]
     }
-
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    #[avro(namespace = "com.testing.namespace")]
-    /// A Documented Record
-    struct TestBasicWithOuterDocAttributes {
-        #[avro(doc = "Milliseconds since Queen released Bohemian Rhapsody")]
-        a: i32,
-        #[avro(doc = "Full lyrics of Bohemian Rhapsody")]
-        b: String,
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    if let Schema::Record(RecordSchema { name, doc, .. }) = TestBasicWithAttributes::get_schema() {
+        assert_eq!("com.testing.namespace".to_owned(), name.namespace.unwrap());
+        assert_eq!("A Documented Record", doc.unwrap())
+    } else {
+        panic!("TestBasicWithAttributes schema must be a record schema")
     }
+    assert_eq!(schema, TestBasicWithAttributes::get_schema());
+}
 
-    #[test]
-    fn test_basic_with_out_doc_attributes() {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"com.testing.namespace.TestBasicWithOuterDocAttributes",
-            "doc":"A Documented Record",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"int",
-                    "doc":"Milliseconds since Queen released Bohemian Rhapsody"
-                },
-                {
-                    "name":"b",
-                    "type": "string",
-                    "doc": "Full lyrics of Bohemian Rhapsody"
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        let derived_schema = TestBasicWithOuterDocAttributes::get_schema();
-        assert_eq!(&schema, &derived_schema);
-        if let Schema::Record(RecordSchema { name, doc, .. }) = derived_schema {
-            assert_eq!("com.testing.namespace".to_owned(), name.namespace.unwrap());
-            assert_eq!("A Documented Record", doc.unwrap())
-        } else {
-            panic!("TestBasicWithOuterDocAttributes schema must be a record schema")
-        }
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+#[avro(namespace = "com.testing.namespace")]
+/// A Documented Record
+struct TestBasicWithOuterDocAttributes {
+    #[avro(doc = "Milliseconds since Queen released Bohemian Rhapsody")]
+    a: i32,
+    #[avro(doc = "Full lyrics of Bohemian Rhapsody")]
+    b: String,
+}
+
+#[test]
+fn test_basic_with_out_doc_attributes() {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"com.testing.namespace.TestBasicWithOuterDocAttributes",
+        "doc":"A Documented Record",
+        "fields":[
+            {
+                "name":"a",
+                "type":"int",
+                "doc":"Milliseconds since Queen released Bohemian Rhapsody"
+            },
+            {
+                "name":"b",
+                "type": "string",
+                "doc": "Full lyrics of Bohemian Rhapsody"
+            }
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    let derived_schema = TestBasicWithOuterDocAttributes::get_schema();
+    assert_eq!(&schema, &derived_schema);
+    if let Schema::Record(RecordSchema { name, doc, .. }) = derived_schema {
+        assert_eq!("com.testing.namespace".to_owned(), name.namespace.unwrap());
+        assert_eq!("A Documented Record", doc.unwrap())
+    } else {
+        panic!("TestBasicWithOuterDocAttributes schema must be a record schema")
+    }
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    #[avro(namespace = "com.testing.namespace")]
-    /// A Documented Record
-    /// that spans
-    /// multiple lines
-    struct TestBasicWithLargeDoc {
-        #[avro(doc = "Milliseconds since Queen released Bohemian Rhapsody")]
-        a: i32,
-        #[avro(doc = "Full lyrics of Bohemian Rhapsody")]
-        b: String,
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+#[avro(namespace = "com.testing.namespace")]
+/// A Documented Record
+/// that spans
+/// multiple lines
+struct TestBasicWithLargeDoc {
+    #[avro(doc = "Milliseconds since Queen released Bohemian Rhapsody")]
+    a: i32,
+    #[avro(doc = "Full lyrics of Bohemian Rhapsody")]
+    b: String,
+}
+
+#[test]
+fn test_basic_with_large_doc() {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"com.testing.namespace.TestBasicWithLargeDoc",
+        "doc":"A Documented Record",
+        "fields":[
+            {
+                "name":"a",
+                "type":"int",
+                "doc":"Milliseconds since Queen released Bohemian Rhapsody"
+            },
+            {
+                "name":"b",
+                "type": "string",
+                "doc": "Full lyrics of Bohemian Rhapsody"
+            }
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    if let Schema::Record(RecordSchema { name, doc, .. }) = TestBasicWithLargeDoc::get_schema() {
+        assert_eq!("com.testing.namespace".to_owned(), name.namespace.unwrap());
+        assert_eq!(
+            "A Documented Record\nthat spans\nmultiple lines",
+            doc.unwrap()
+        )
+    } else {
+        panic!("TestBasicWithLargeDoc schema must be a record schema")
+    }
+    assert_eq!(schema, TestBasicWithLargeDoc::get_schema());
+}
 
-    #[test]
-    fn test_basic_with_large_doc() {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"com.testing.namespace.TestBasicWithLargeDoc",
-            "doc":"A Documented Record",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"int",
-                    "doc":"Milliseconds since Queen released Bohemian Rhapsody"
-                },
-                {
-                    "name":"b",
-                    "type": "string",
-                    "doc": "Full lyrics of Bohemian Rhapsody"
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        if let Schema::Record(RecordSchema { name, doc, .. }) = TestBasicWithLargeDoc::get_schema()
-        {
-            assert_eq!("com.testing.namespace".to_owned(), name.namespace.unwrap());
-            assert_eq!(
-                "A Documented Record\nthat spans\nmultiple lines",
-                doc.unwrap()
-            )
-        } else {
-            panic!("TestBasicWithLargeDoc schema must be a record schema")
-        }
-        assert_eq!(schema, TestBasicWithLargeDoc::get_schema());
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+struct TestBasicWithBool {
+    a: bool,
+    b: Option<bool>,
+}
+
+proptest! {
+#[test]
+fn avro_3634_test_basic_with_bool(a in any::<bool>(), b in any::<Option<bool>>()) {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestBasicWithBool",
+        "fields":[
+            {
+                "name":"a",
+                "type":"boolean"
+            },
+            {
+                "name":"b",
+                "type":["null","boolean"]
+            }
+        ]
+    }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    let derived_schema = TestBasicWithBool::get_schema();
+
+    if let Schema::Record(RecordSchema { name, .. }) = derived_schema {
+        assert_eq!("TestBasicWithBool", name.fullname(None))
+    } else {
+        panic!("TestBasicWithBool schema must be a record schema")
     }
+    assert_eq!(schema, TestBasicWithBool::get_schema());
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    struct TestBasicWithBool {
-        a: bool,
-        b: Option<bool>,
-    }
-
-    proptest! {
-    #[test]
-    fn avro_3634_test_basic_with_bool(a in any::<bool>(), b in any::<Option<bool>>()) {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestBasicWithBool",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"boolean"
-                },
-                {
-                    "name":"b",
-                    "type":["null","boolean"]
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        let derived_schema = TestBasicWithBool::get_schema();
+    serde_assert(TestBasicWithBool { a, b });
+}}
 
-        if let Schema::Record(RecordSchema { name, .. }) = derived_schema {
-            assert_eq!("TestBasicWithBool", name.fullname(None))
-        } else {
-            panic!("TestBasicWithBool schema must be a record schema")
-        }
-        assert_eq!(schema, TestBasicWithBool::get_schema());
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+struct TestBasicWithU32 {
+    a: u32,
+}
 
-        serde_assert(TestBasicWithBool { a, b });
-    }}
+proptest! {
+#[test]
+fn test_basic_with_u32(a in any::<u32>()) {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestBasicWithU32",
+        "fields":[
+            {
+                "name":"a",
+                "type":"long"
+            }
+        ]
+    }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    if let Schema::Record(RecordSchema { name, .. }) = TestBasicWithU32::get_schema() {
+        assert_eq!("TestBasicWithU32", name.fullname(None))
+    } else {
+        panic!("TestBasicWithU32 schema must be a record schema")
+    }
+    assert_eq!(schema, TestBasicWithU32::get_schema());
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    struct TestBasicWithU32 {
-        a: u32,
-    }
-
-    proptest! {
-    #[test]
-    fn test_basic_with_u32(a in any::<u32>()) {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestBasicWithU32",
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"long"
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        if let Schema::Record(RecordSchema { name, .. }) = TestBasicWithU32::get_schema() {
-            assert_eq!("TestBasicWithU32", name.fullname(None))
-        } else {
-            panic!("TestBasicWithU32 schema must be a record schema")
-        }
-        assert_eq!(schema, TestBasicWithU32::get_schema());
+    serde_assert(TestBasicWithU32 { a });
+}}
 
-        serde_assert(TestBasicWithU32 { a });
-    }}
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+#[avro(alias = "a", alias = "b", alias = "c")]
+struct TestBasicStructWithAliases {
+    a: i32,
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    #[avro(alias = "a", alias = "b", alias = "c")]
-    struct TestBasicStructWithAliases {
-        a: i32,
+#[test]
+fn test_basic_struct_with_aliases() {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestBasicStructWithAliases",
+        "aliases":["a", "b", "c"],
+        "fields":[
+            {
+                "name":"a",
+                "type":"int"
+            }
+        ]
+    }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    if let Schema::Record(RecordSchema { name, aliases, .. }) =
+        TestBasicStructWithAliases::get_schema()
+    {
+        assert_eq!("TestBasicStructWithAliases", name.fullname(None));
+        assert_eq!(
+            Some(vec![
+                Alias::new("a").unwrap(),
+                Alias::new("b").unwrap(),
+                Alias::new("c").unwrap()
+            ]),
+            aliases
+        );
+    } else {
+        panic!("TestBasicStructWithAliases schema must be a record schema")
     }
+    assert_eq!(schema, TestBasicStructWithAliases::get_schema());
 
-    #[test]
-    fn test_basic_struct_with_aliases() {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestBasicStructWithAliases",
-            "aliases":["a", "b", "c"],
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"int"
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        if let Schema::Record(RecordSchema { name, aliases, .. }) =
-            TestBasicStructWithAliases::get_schema()
-        {
-            assert_eq!("TestBasicStructWithAliases", name.fullname(None));
-            assert_eq!(
-                Some(vec![
-                    Alias::new("a").unwrap(),
-                    Alias::new("b").unwrap(),
-                    Alias::new("c").unwrap()
-                ]),
-                aliases
-            );
-        } else {
-            panic!("TestBasicStructWithAliases schema must be a record schema")
-        }
-        assert_eq!(schema, TestBasicStructWithAliases::get_schema());
+    serde_assert(TestBasicStructWithAliases { a: i32::MAX });
+}
 
-        serde_assert(TestBasicStructWithAliases { a: i32::MAX });
-    }
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+#[avro(alias = "d")]
+#[avro(alias = "e")]
+#[avro(alias = "f")]
+struct TestBasicStructWithAliases2 {
+    a: i32,
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    #[avro(alias = "d")]
-    #[avro(alias = "e")]
-    #[avro(alias = "f")]
-    struct TestBasicStructWithAliases2 {
-        a: i32,
+#[test]
+fn test_basic_struct_with_aliases2() {
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestBasicStructWithAliases2",
+        "aliases":["d", "e", "f"],
+        "fields":[
+            {
+                "name":"a",
+                "type":"int"
+            }
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    if let Schema::Record(RecordSchema { name, aliases, .. }) =
+        TestBasicStructWithAliases2::get_schema()
+    {
+        assert_eq!("TestBasicStructWithAliases2", name.fullname(None));
+        assert_eq!(
+            Some(vec![
+                Alias::new("d").unwrap(),
+                Alias::new("e").unwrap(),
+                Alias::new("f").unwrap()
+            ]),
+            aliases
+        );
+    } else {
+        panic!("TestBasicStructWithAliases2 schema must be a record schema")
+    }
+    assert_eq!(schema, TestBasicStructWithAliases2::get_schema());
 
-    #[test]
-    fn test_basic_struct_with_aliases2() {
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestBasicStructWithAliases2",
-            "aliases":["d", "e", "f"],
-            "fields":[
-                {
-                    "name":"a",
-                    "type":"int"
-                }
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        if let Schema::Record(RecordSchema { name, aliases, .. }) =
-            TestBasicStructWithAliases2::get_schema()
-        {
-            assert_eq!("TestBasicStructWithAliases2", name.fullname(None));
-            assert_eq!(
-                Some(vec![
-                    Alias::new("d").unwrap(),
-                    Alias::new("e").unwrap(),
-                    Alias::new("f").unwrap()
-                ]),
-                aliases
-            );
-        } else {
-            panic!("TestBasicStructWithAliases2 schema must be a record schema")
-        }
-        assert_eq!(schema, TestBasicStructWithAliases2::get_schema());
+    serde_assert(TestBasicStructWithAliases2 { a: i32::MAX });
+}
 
-        serde_assert(TestBasicStructWithAliases2 { a: i32::MAX });
-    }
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+#[avro(alias = "a", alias = "b", alias = "c")]
+enum TestBasicEnumWithAliases {
+    A,
+    B,
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    #[avro(alias = "a", alias = "b", alias = "c")]
-    enum TestBasicEnumWithAliases {
-        A,
-        B,
+#[test]
+fn test_basic_enum_with_aliases() {
+    let schema = r#"
+    {
+        "type":"enum",
+        "name":"TestBasicEnumWithAliases",
+        "aliases":["a", "b", "c"],
+        "symbols":[
+            "A",
+            "B"
+        ]
+    }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    if let Schema::Enum(EnumSchema { name, aliases, .. }) = TestBasicEnumWithAliases::get_schema() {
+        assert_eq!("TestBasicEnumWithAliases", name.fullname(None));
+        assert_eq!(
+            Some(vec![
+                Alias::new("a").unwrap(),
+                Alias::new("b").unwrap(),
+                Alias::new("c").unwrap()
+            ]),
+            aliases
+        );
+    } else {
+        panic!("TestBasicEnumWithAliases schema must be an enum schema")
     }
+    assert_eq!(schema, TestBasicEnumWithAliases::get_schema());
 
-    #[test]
-    fn test_basic_enum_with_aliases() {
-        let schema = r#"
-        {
-            "type":"enum",
-            "name":"TestBasicEnumWithAliases",
-            "aliases":["a", "b", "c"],
-            "symbols":[
-                "A",
-                "B"
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        if let Schema::Enum(EnumSchema { name, aliases, .. }) =
-            TestBasicEnumWithAliases::get_schema()
-        {
-            assert_eq!("TestBasicEnumWithAliases", name.fullname(None));
-            assert_eq!(
-                Some(vec![
-                    Alias::new("a").unwrap(),
-                    Alias::new("b").unwrap(),
-                    Alias::new("c").unwrap()
-                ]),
-                aliases
-            );
-        } else {
-            panic!("TestBasicEnumWithAliases schema must be an enum schema")
-        }
-        assert_eq!(schema, TestBasicEnumWithAliases::get_schema());
+    serde_assert(TestBasicEnumWithAliases::A);
+}
 
-        serde_assert(TestBasicEnumWithAliases::A);
-    }
+#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
+#[avro(alias = "d")]
+#[avro(alias = "e")]
+#[avro(alias = "f")]
+enum TestBasicEnumWithAliases2 {
+    A,
+    B,
+}
 
-    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq, Eq)]
-    #[avro(alias = "d")]
-    #[avro(alias = "e")]
-    #[avro(alias = "f")]
-    enum TestBasicEnumWithAliases2 {
-        A,
-        B,
+#[test]
+fn test_basic_enum_with_aliases2() {
+    let schema = r#"
+    {
+        "type":"enum",
+        "name":"TestBasicEnumWithAliases2",
+        "aliases":["d", "e", "f"],
+        "symbols":[
+            "A",
+            "B"
+        ]
     }
+    "#;
+    let schema = Schema::parse_str(schema).unwrap();
+    if let Schema::Enum(EnumSchema { name, aliases, .. }) = TestBasicEnumWithAliases2::get_schema()
+    {
+        assert_eq!("TestBasicEnumWithAliases2", name.fullname(None));
+        assert_eq!(
+            Some(vec![
+                Alias::new("d").unwrap(),
+                Alias::new("e").unwrap(),
+                Alias::new("f").unwrap()
+            ]),
+            aliases
+        );
+    } else {
+        panic!("TestBasicEnumWithAliases2 schema must be an enum schema")
+    }
+    assert_eq!(schema, TestBasicEnumWithAliases2::get_schema());
 
-    #[test]
-    fn test_basic_enum_with_aliases2() {
-        let schema = r#"
-        {
-            "type":"enum",
-            "name":"TestBasicEnumWithAliases2",
-            "aliases":["d", "e", "f"],
-            "symbols":[
-                "A",
-                "B"
-            ]
-        }
-        "#;
-        let schema = Schema::parse_str(schema).unwrap();
-        if let Schema::Enum(EnumSchema { name, aliases, .. }) =
-            TestBasicEnumWithAliases2::get_schema()
-        {
-            assert_eq!("TestBasicEnumWithAliases2", name.fullname(None));
-            assert_eq!(
-                Some(vec![
-                    Alias::new("d").unwrap(),
-                    Alias::new("e").unwrap(),
-                    Alias::new("f").unwrap()
-                ]),
-                aliases
-            );
-        } else {
-            panic!("TestBasicEnumWithAliases2 schema must be an enum schema")
-        }
-        assert_eq!(schema, TestBasicEnumWithAliases2::get_schema());
+    serde_assert(TestBasicEnumWithAliases2::B);
+}
 
-        serde_assert(TestBasicEnumWithAliases2::B);
+#[test]
+fn test_basic_struct_with_defaults() {
+    #[derive(Debug, Deserialize, Serialize, AvroSchema, Clone, PartialEq, Eq)]
+    enum MyEnum {
+        Foo,
+        Bar,
+        Baz,
     }
 
-    #[test]
-    fn test_basic_struct_with_defaults() {
-        #[derive(Debug, Deserialize, Serialize, AvroSchema, Clone, PartialEq, Eq)]
-        enum MyEnum {
-            Foo,
-            Bar,
-            Baz,
-        }
+    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+    struct TestBasicStructWithDefaultValues {
+        #[avro(default = "123")]
+        a: i32,
+        #[avro(default = r#""The default value for 'b'""#)]
+        b: String,
+        #[avro(default = "true")]
+        condition: bool,
+        // no default value for 'c'
+        c: f64,
+        #[avro(default = r#"{"a": 1, "b": 2}"#)]
+        map: HashMap<String, i32>,
 
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-        struct TestBasicStructWithDefaultValues {
-            #[avro(default = "123")]
-            a: i32,
-            #[avro(default = r#""The default value for 'b'""#)]
-            b: String,
-            #[avro(default = "true")]
-            condition: bool,
-            // no default value for 'c'
-            c: f64,
-            #[avro(default = r#"{"a": 1, "b": 2}"#)]
-            map: HashMap<String, i32>,
-
-            #[avro(default = "[1, 2, 3]")]
-            array: Vec<i32>,
-
-            #[avro(default = r#""Foo""#)]
-            myenum: MyEnum,
-
-            #[avro(default = "null")]
-            optional: Option<String>,
-        }
+        #[avro(default = "[1, 2, 3]")]
+        array: Vec<i32>,
 
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestBasicStructWithDefaultValues",
-            "fields": [
-                {
-                    "name":"a",
-                    "type":"int",
-                    "default":123
-                },
-                {
-                    "name":"b",
-                    "type":"string",
-                    "default": "The default value for 'b'"
+        #[avro(default = r#""Foo""#)]
+        myenum: MyEnum,
+
+        #[avro(default = "null")]
+        optional: Option<String>,
+    }
+
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestBasicStructWithDefaultValues",
+        "fields": [
+            {
+                "name":"a",
+                "type":"int",
+                "default":123
+            },
+            {
+                "name":"b",
+                "type":"string",
+                "default": "The default value for 'b'"
+            },
+            {
+                "name":"condition",
+                "type":"boolean",
+                "default":true
+            },
+            {
+                "name":"c",
+                "type":"double"
+            },
+            {
+                "name":"map",
+                "type":{
+                    "type":"map",
+                    "values":"int"
                 },
-                {
-                    "name":"condition",
-                    "type":"boolean",
-                    "default":true
+                "default": {
+                    "a": 1,
+                    "b": 2
+                }
+            },
+            {
+                "name":"array",
+                "type":{
+                    "type":"array",
+                    "items":"int"
                 },
-                {
-                    "name":"c",
-                    "type":"double"
+                "default": [1, 2, 3]
+            },
+            {
+                "name":"myenum",
+                "type":{
+                    "type":"enum",
+                    "name":"MyEnum",
+                    "symbols":["Foo", "Bar", "Baz"]
                 },
-                {
-                    "name":"map",
-                    "type":{
-                        "type":"map",
-                        "values":"int"
-                    },
-                    "default": {
+                "default":"Foo"
+            },
+            {
+                "name":"optional",
+                "type": ["null", "string"],
+                "default": null
+            }
+        ]
+    }
+    "#;
+
+    let schema = Schema::parse_str(schema).unwrap();
+    if let Schema::Record(RecordSchema { name, fields, .. }) =
+        TestBasicStructWithDefaultValues::get_schema()
+    {
+        assert_eq!("TestBasicStructWithDefaultValues", name.fullname(None));
+        use serde_json::json;
+        for field in fields {
+            match field.name.as_str() {
+                "a" => assert_eq!(Some(json!(123_i32)), field.default),
+                "b" => assert_eq!(
+                    Some(json!(r#"The default value for 'b'"#.to_owned())),
+                    field.default
+                ),
+                "condition" => assert_eq!(Some(json!(true)), field.default),
+                "array" => assert_eq!(Some(json!([1, 2, 3])), field.default),
+                "map" => assert_eq!(
+                    Some(json!({
                         "a": 1,
                         "b": 2
-                    }
-                },
-                {
-                    "name":"array",
-                    "type":{
-                        "type":"array",
-                        "items":"int"
-                    },
-                    "default": [1, 2, 3]
-                },
-                {
-                    "name":"myenum",
-                    "type":{
-                        "type":"enum",
-                        "name":"MyEnum",
-                        "symbols":["Foo", "Bar", "Baz"]
-                    },
-                    "default":"Foo"
-                },
-                {
-                    "name":"optional",
-                    "type": ["null", "string"],
-                    "default": null
-                }
-            ]
-        }
-        "#;
-
-        let schema = Schema::parse_str(schema).unwrap();
-        if let Schema::Record(RecordSchema { name, fields, .. }) =
-            TestBasicStructWithDefaultValues::get_schema()
-        {
-            assert_eq!("TestBasicStructWithDefaultValues", name.fullname(None));
-            use serde_json::json;
-            for field in fields {
-                match field.name.as_str() {
-                    "a" => assert_eq!(Some(json!(123_i32)), field.default),
-                    "b" => assert_eq!(
-                        Some(json!(r#"The default value for 'b'"#.to_owned())),
-                        field.default
-                    ),
-                    "condition" => assert_eq!(Some(json!(true)), field.default),
-                    "array" => assert_eq!(Some(json!([1, 2, 3])), field.default),
-                    "map" => assert_eq!(
-                        Some(json!({
-                            "a": 1,
-                            "b": 2
-                        })),
-                        field.default
-                    ),
-                    "c" => assert_eq!(None, field.default),
-                    "myenum" => assert_eq!(Some(json!("Foo")), field.default),
-                    "optional" => assert_eq!(Some(json!(null)), field.default),
-                    _ => panic!("Unexpected field name"),
-                }
+                    })),
+                    field.default
+                ),
+                "c" => assert_eq!(None, field.default),
+                "myenum" => assert_eq!(Some(json!("Foo")), field.default),
+                "optional" => assert_eq!(Some(json!(null)), field.default),
+                _ => panic!("Unexpected field name"),
             }
-        } else {
-            panic!("TestBasicStructWithDefaultValues schema must be a record schema")
-        }
-        assert_eq!(schema, TestBasicStructWithDefaultValues::get_schema());
-
-        serde_assert(TestBasicStructWithDefaultValues {
-            a: 321,
-            b: "A custom value for 'b'".to_owned(),
-            condition: false,
-            c: 987.654,
-            map: [("a".to_owned(), 1), ("b".to_owned(), 2)]
-                .iter()
-                .cloned()
-                .collect(),
-            array: vec![4, 5, 6],
-            myenum: MyEnum::Bar,
-            optional: None,
-        });
-    }
-
-    #[test]
-    fn avro_3633_test_basic_struct_with_skip_attribute() {
-        // Note: If using the skip attribute together with serialization,
-        // the serde's skip attribute needs also to be added
-
-        #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
-        struct TestBasicStructNoSchema {
-            field: bool,
         }
+    } else {
+        panic!("TestBasicStructWithDefaultValues schema must be a record schema")
+    }
+    assert_eq!(schema, TestBasicStructWithDefaultValues::get_schema());
+
+    serde_assert(TestBasicStructWithDefaultValues {
+        a: 321,
+        b: "A custom value for 'b'".to_owned(),
+        condition: false,
+        c: 987.654,
+        map: [("a".to_owned(), 1), ("b".to_owned(), 2)]
+            .iter()
+            .cloned()
+            .collect(),
+        array: vec![4, 5, 6],
+        myenum: MyEnum::Bar,
+        optional: None,
+    });
+}
 
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-        struct TestBasicStructWithSkipAttribute {
-            #[avro(skip)]
-            #[serde(skip)]
-            condition: bool,
-            #[avro(skip = false)]
-            a: f64,
-            #[avro(skip)]
-            #[serde(skip)]
-            map: HashMap<String, i32>,
-            array: Vec<i32>,
-            #[avro(skip = true)]
-            #[serde(skip)]
-            mystruct: TestBasicStructNoSchema,
-            b: i32,
-        }
+#[test]
+fn avro_3633_test_basic_struct_with_skip_attribute() {
+    // Note: If using the skip attribute together with serialization,
+    // the serde's skip attribute needs also to be added
 
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestBasicStructWithSkipAttribute",
-            "fields": [
-                {
-                    "name":"a",
-                    "type":"double"
-                },
-                {
-                    "name":"array",
-                    "type":{
-                        "type":"array",
-                        "items":"int"
-                    }
-                },
-                {
-                    "name":"b",
-                    "type":"int"
-                }
-            ]
-        }
-        "#;
-
-        let schema = Schema::parse_str(schema).unwrap();
-        let derived_schema = TestBasicStructWithSkipAttribute::get_schema();
-        if let Schema::Record(RecordSchema { name, fields, .. }) = &derived_schema {
-            assert_eq!("TestBasicStructWithSkipAttribute", name.fullname(None));
-            for field in fields {
-                match field.name.as_str() {
-                    "condition" => panic!("Unexpected field 'condition'"),
-                    "mystruct" => panic!("Unexpected field 'mystruct'"),
-                    "map" => panic!("Unexpected field 'map'"),
-                    _ => {}
-                }
-            }
-        } else {
-            panic!(
-                "TestBasicStructWithSkipAttribute schema must be a record schema: {derived_schema:?}"
-            )
-        }
-        assert_eq!(schema, derived_schema);
-
-        // Note: If serde's `skip` attribute is used on a field, the field's type
-        // needs the trait 'Default' to be implemented, since it is skipping the serialization process.
-        // Copied or cloned objects within 'serde_assert()' doesn't "copy" (serialize/deserialze)
-        // these fields, so no values are initialized here for skipped fields.
-        serde_assert(TestBasicStructWithSkipAttribute {
-            condition: bool::default(), // <- skipped
-            a: 987.654,
-            map: HashMap::default(), // <- skipped
-            array: vec![4, 5, 6],
-            mystruct: TestBasicStructNoSchema::default(), // <- skipped
-            b: 321,
-        });
-    }
-
-    #[test]
-    fn avro_3633_test_basic_struct_with_rename_attribute() {
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-        struct TestBasicStructWithRenameAttribute {
-            #[avro(rename = "a1")]
-            #[serde(rename = "a1")]
-            a: bool,
-            b: i32,
-            #[avro(rename = "c1")]
-            #[serde(rename = "c1")]
-            c: f32,
-        }
+    #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
+    struct TestBasicStructNoSchema {
+        field: bool,
+    }
 
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"TestBasicStructWithRenameAttribute",
-            "fields": [
-                {
-                    "name":"a1",
-                    "type":"boolean"
-                },
-                {
-                    "name":"b",
-                    "type":"int"
-                },
-                {
-                    "name":"c1",
-                    "type":"float"
-                }
-            ]
-        }
-        "#;
-
-        let schema = Schema::parse_str(schema).unwrap();
-        let derived_schema = TestBasicStructWithRenameAttribute::get_schema();
-        if let Schema::Record(RecordSchema { name, fields, .. }) = &derived_schema {
-            assert_eq!("TestBasicStructWithRenameAttribute", name.fullname(None));
-            for field in fields {
-                match field.name.as_str() {
-                    "a" => panic!("Unexpected field name 'a': must be 'a1'"),
-                    "c" => panic!("Unexpected field name 'c': must be 'c1'"),
-                    _ => {}
+    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+    struct TestBasicStructWithSkipAttribute {
+        #[avro(skip)]
+        #[serde(skip)]
+        condition: bool,
+        #[avro(skip = false)]
+        a: f64,
+        #[avro(skip)]
+        #[serde(skip)]
+        map: HashMap<String, i32>,
+        array: Vec<i32>,
+        #[avro(skip = true)]
+        #[serde(skip)]
+        mystruct: TestBasicStructNoSchema,
+        b: i32,
+    }
+
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestBasicStructWithSkipAttribute",
+        "fields": [
+            {
+                "name":"a",
+                "type":"double"
+            },
+            {
+                "name":"array",
+                "type":{
+                    "type":"array",
+                    "items":"int"
                 }
+            },
+            {
+                "name":"b",
+                "type":"int"
+            }
+        ]
+    }
+    "#;
+
+    let schema = Schema::parse_str(schema).unwrap();
+    let derived_schema = TestBasicStructWithSkipAttribute::get_schema();
+    if let Schema::Record(RecordSchema { name, fields, .. }) = &derived_schema {
+        assert_eq!("TestBasicStructWithSkipAttribute", name.fullname(None));
+        for field in fields {
+            match field.name.as_str() {
+                "condition" => panic!("Unexpected field 'condition'"),
+                "mystruct" => panic!("Unexpected field 'mystruct'"),
+                "map" => panic!("Unexpected field 'map'"),
+                _ => {}
             }
-        } else {
-            panic!(
-                "TestBasicStructWithRenameAttribute schema must be a record schema: {derived_schema:?}"
-            )
         }
-        assert_eq!(schema, derived_schema);
-
-        serde_assert(TestBasicStructWithRenameAttribute {
-            a: true,
-            b: 321,
-            c: 987.654,
-        });
+    } else {
+        panic!(
+            "TestBasicStructWithSkipAttribute schema must be a record schema: {derived_schema:?}"
+        )
     }
+    assert_eq!(schema, derived_schema);
+
+    // Note: If serde's `skip` attribute is used on a field, the field's type
+    // needs the trait 'Default' to be implemented, since it is skipping the serialization process.
+    // Copied or cloned objects within 'serde_assert()' doesn't "copy" (serialize/deserialze)
+    // these fields, so no values are initialized here for skipped fields.
+    serde_assert(TestBasicStructWithSkipAttribute {
+        condition: bool::default(), // <- skipped
+        a: 987.654,
+        map: HashMap::default(), // <- skipped
+        array: vec![4, 5, 6],
+        mystruct: TestBasicStructNoSchema::default(), // <- skipped
+        b: 321,
+    });
+}
 
-    #[test]
-    fn test_avro_3663_raw_identifier_field_name() {
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-        struct TestRawIdent {
-            r#type: bool,
-        }
+#[test]
+fn avro_3633_test_basic_struct_with_rename_attribute() {
+    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+    struct TestBasicStructWithRenameAttribute {
+        #[avro(rename = "a1")]
+        #[serde(rename = "a1")]
+        a: bool,
+        b: i32,
+        #[avro(rename = "c1")]
+        #[serde(rename = "c1")]
+        c: f32,
+    }
 
-        let derived_schema = TestRawIdent::get_schema();
-        if let Schema::Record(RecordSchema { fields, .. }) = derived_schema {
-            let field = fields.first().expect("TestRawIdent must contain a field");
-            assert_eq!(field.name, "type");
-        } else {
-            panic!("Unexpected schema type for {derived_schema:?}")
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"TestBasicStructWithRenameAttribute",
+        "fields": [
+            {
+                "name":"a1",
+                "type":"boolean"
+            },
+            {
+                "name":"b",
+                "type":"int"
+            },
+            {
+                "name":"c1",
+                "type":"float"
+            }
+        ]
+    }
+    "#;
+
+    let schema = Schema::parse_str(schema).unwrap();
+    let derived_schema = TestBasicStructWithRenameAttribute::get_schema();
+    if let Schema::Record(RecordSchema { name, fields, .. }) = &derived_schema {
+        assert_eq!("TestBasicStructWithRenameAttribute", name.fullname(None));
+        for field in fields {
+            match field.name.as_str() {
+                "a" => panic!("Unexpected field name 'a': must be 'a1'"),
+                "c" => panic!("Unexpected field name 'c': must be 'c1'"),
+                _ => {}
+            }
         }
+    } else {
+        panic!(
+            "TestBasicStructWithRenameAttribute schema must be a record schema: {derived_schema:?}"
+        )
     }
+    assert_eq!(schema, derived_schema);
 
-    #[test]
-    fn avro_3962_fields_documentation() {
-        /// Foo docs
-        #[derive(AvroSchema)]
-        #[allow(dead_code)]
-        struct Foo {
-            /// a's Rustdoc
-            a: i32,
-            /// b's Rustdoc
-            #[avro(doc = "attribute doc has priority over Rustdoc")]
-            b: i32,
-        }
+    serde_assert(TestBasicStructWithRenameAttribute {
+        a: true,
+        b: 321,
+        c: 987.654,
+    });
+}
 
-        if let Schema::Record(RecordSchema { fields, .. }) = Foo::get_schema() {
-            assert_eq!(fields[0].doc, Some("a's Rustdoc".to_string()));
-            assert_eq!(
-                fields[1].doc,
-                Some("attribute doc has priority over Rustdoc".to_string())
-            );
-        } else {
-            panic!("Unexpected schema type for Foo")
-        }
+#[test]
+fn test_avro_3663_raw_identifier_field_name() {
+    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+    struct TestRawIdent {
+        r#type: bool,
     }
 
-    #[test]
-    fn avro_rs_247_serde_flatten_support() {
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-        struct Nested {
-            a: bool,
-        }
+    let derived_schema = TestRawIdent::get_schema();
+    if let Schema::Record(RecordSchema { fields, .. }) = derived_schema {
+        let field = fields.first().expect("TestRawIdent must contain a field");
+        assert_eq!(field.name, "type");
+    } else {
+        panic!("Unexpected schema type for {derived_schema:?}")
+    }
+}
 
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-        struct Foo {
-            #[serde(flatten)]
-            #[avro(flatten)]
-            nested: Nested,
-            b: i32,
-        }
+#[test]
+fn avro_3962_fields_documentation() {
+    /// Foo docs
+    #[derive(AvroSchema)]
+    #[allow(dead_code)]
+    struct Foo {
+        /// a's Rustdoc
+        a: i32,
+        /// b's Rustdoc
+        #[avro(doc = "attribute doc has priority over Rustdoc")]
+        b: i32,
+    }
 
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"Foo",
-            "fields": [
-                {
-                    "name":"a",
-                    "type":"boolean"
-                },
-                {
-                    "name":"b",
-                    "type":"int"
-                }
-            ]
-        }
-        "#;
+    if let Schema::Record(RecordSchema { fields, .. }) = Foo::get_schema() {
+        assert_eq!(fields[0].doc, Some("a's Rustdoc".to_string()));
+        assert_eq!(
+            fields[1].doc,
+            Some("attribute doc has priority over Rustdoc".to_string())
+        );
+    } else {
+        panic!("Unexpected schema type for Foo")
+    }
+}
 
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, Foo::get_schema());
+#[test]
+fn avro_rs_247_serde_flatten_support() {
+    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+    struct Nested {
+        a: bool,
+    }
 
-        serde_assert(Foo {
-            nested: Nested { a: true },
-            b: 321,
-        });
+    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+    struct Foo {
+        #[serde(flatten)]
+        #[avro(flatten)]
+        nested: Nested,
+        b: i32,
     }
 
-    #[test]
-    fn avro_rs_247_serde_nested_flatten_support() {
-        use apache_avro::AvroSchema;
-        use serde::{Deserialize, Serialize};
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"Foo",
+        "fields": [
+            {
+                "name":"a",
+                "type":"boolean"
+            },
+            {
+                "name":"b",
+                "type":"int"
+            }
+        ]
+    }
+    "#;
 
-        #[derive(AvroSchema, Debug, Clone, PartialEq, Serialize, Deserialize)]
-        pub struct NestedFoo {
-            one: u32,
-        }
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, Foo::get_schema());
 
-        #[derive(AvroSchema, Debug, Clone, PartialEq, Serialize, Deserialize)]
-        pub struct Foo {
-            #[serde(flatten)]
-            #[avro(flatten)]
-            nested_foo: NestedFoo,
-        }
+    serde_assert(Foo {
+        nested: Nested { a: true },
+        b: 321,
+    });
+}
 
-        #[derive(AvroSchema, Debug, Clone, PartialEq, Serialize, Deserialize)]
-        struct Bar {
-            foo: Foo,
-            two: u32,
-        }
+#[test]
+fn avro_rs_247_serde_nested_flatten_support() {
+    use apache_avro::AvroSchema;
+    use serde::{Deserialize, Serialize};
 
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"Bar",
-            "fields": [
-                {
-                    "name":"foo",
-                    "type": {
-                        "type": "record",
-                        "name": "Foo",
-                        "fields": [
-                            {
-                                "name": "one",
-                                "type": "long"
-                            }
-                        ]
-                    }
-                },
-                {
-                    "name":"two",
-                    "type":"long"
-                }
-            ]
-        }
-        "#;
+    #[derive(AvroSchema, Debug, Clone, PartialEq, Serialize, Deserialize)]
+    pub struct NestedFoo {
+        one: u32,
+    }
 
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, Bar::get_schema());
+    #[derive(AvroSchema, Debug, Clone, PartialEq, Serialize, Deserialize)]
+    pub struct Foo {
+        #[serde(flatten)]
+        #[avro(flatten)]
+        nested_foo: NestedFoo,
+    }
 
-        serde_assert(Bar {
-            foo: Foo {
-                nested_foo: NestedFoo { one: 42 },
+    #[derive(AvroSchema, Debug, Clone, PartialEq, Serialize, Deserialize)]
+    struct Bar {
+        foo: Foo,
+        two: u32,
+    }
+
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"Bar",
+        "fields": [
+            {
+                "name":"foo",
+                "type": {
+                    "type": "record",
+                    "name": "Foo",
+                    "fields": [
+                        {
+                            "name": "one",
+                            "type": "long"
+                        }
+                    ]
+                }
             },
-            two: 2,
-        });
+            {
+                "name":"two",
+                "type":"long"
+            }
+        ]
     }
+    "#;
 
-    #[test]
-    #[should_panic(expected = "Duplicate field names found")]
-    fn avro_rs_247_serde_flatten_support_duplicate_field_name() {
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-        struct Nested {
-            a: i32,
-        }
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, Bar::get_schema());
 
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-        struct Foo {
-            #[serde(flatten)]
-            #[avro(flatten)]
-            nested: Nested,
-            a: i32,
-        }
+    serde_assert(Bar {
+        foo: Foo {
+            nested_foo: NestedFoo { one: 42 },
+        },
+        two: 2,
+    });
+}
 
-        Foo::get_schema();
+#[test]
+#[should_panic(expected = "Duplicate field names found")]
+fn avro_rs_247_serde_flatten_support_duplicate_field_name() {
+    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+    struct Nested {
+        a: i32,
     }
 
-    #[test]
-    fn avro_rs_247_serde_flatten_support_with_skip() {
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-        struct Nested {
-            a: bool,
-            #[serde(skip)]
-            #[avro(skip)]
-            c: f64,
-        }
+    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+    struct Foo {
+        #[serde(flatten)]
+        #[avro(flatten)]
+        nested: Nested,
+        a: i32,
+    }
 
-        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
-        struct Foo {
-            #[serde(flatten)]
-            #[avro(flatten)]
-            nested: Nested,
-            b: i32,
-        }
+    Foo::get_schema();
+}
 
-        let schema = r#"
-        {
-            "type":"record",
-            "name":"Foo",
-            "fields": [
-                {
-                    "name":"a",
-                    "type":"boolean"
-                },
-                {
-                    "name":"b",
-                    "type":"int"
-                }
-            ]
-        }
-        "#;
+#[test]
+fn avro_rs_247_serde_flatten_support_with_skip() {
+    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+    struct Nested {
+        a: bool,
+        #[serde(skip)]
+        #[avro(skip)]
+        c: f64,
+    }
 
-        let schema = Schema::parse_str(schema).unwrap();
-        assert_eq!(schema, Foo::get_schema());
+    #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+    struct Foo {
+        #[avro(flatten)]
+        #[serde(flatten)]
+        nested: Nested,
+        b: i32,
+    }
 
-        serde_assert(Foo {
-            nested: Nested { a: true, c: 0.0 },
-            b: 321,
-        });
+    let schema = r#"
+    {
+        "type":"record",
+        "name":"Foo",
+        "fields": [
+            {
+                "name":"a",
+                "type":"boolean"
+            },
+            {
+                "name":"b",
+                "type":"int"
+            }
+        ]
     }
+    "#;
+
+    let schema = Schema::parse_str(schema).unwrap();
+    assert_eq!(schema, Foo::get_schema());
+
+    serde_assert(Foo {
+        nested: Nested { a: true, c: 0.0 },
+        b: 321,
+    });
 }
diff --git a/avro_derive/tests/serde.rs b/avro_derive/tests/serde.rs
new file mode 100644
index 0000000..5e57e78
--- /dev/null
+++ b/avro_derive/tests/serde.rs
@@ -0,0 +1,698 @@
+use apache_avro::{AvroSchema, Error, Reader, Schema, Writer, from_value};
+use serde::{Deserialize, Serialize, de::DeserializeOwned};
+
+/// Takes in a type that implements the right combination of traits and runs it through a Serde Cycle and asserts the result is the same
+fn serde_assert<T>(obj: T)
+where
+    T: std::fmt::Debug + Serialize + DeserializeOwned + AvroSchema + Clone + PartialEq,
+{
+    assert_eq!(obj, serde(obj.clone()).unwrap());
+}
+
+/// Takes in a type that implements the right combination of traits and runs it through a Serde Cycle and asserts that the error matches the expected string
+fn serde_assert_err<T>(obj: T, expected: &str)
+where
+    T: std::fmt::Debug + Serialize + DeserializeOwned + AvroSchema + Clone + PartialEq,
+{
+    let error = serde(obj).unwrap_err().to_string();
+    assert!(
+        error.contains(expected),
+        "Error `{error}` does not contain `{expected}`"
+    );
+}
+
+fn serde<T>(obj: T) -> Result<T, Error>
+where
+    T: Serialize + DeserializeOwned + AvroSchema,
+{
+    de(ser(obj)?)
+}
+
+fn ser<T>(obj: T) -> Result<Vec<u8>, Error>
+where
+    T: Serialize + AvroSchema,
+{
+    let schema = T::get_schema();
+    let mut writer = Writer::new(&schema, Vec::new())?;
+    writer.append_ser(obj)?;
+    writer.into_inner()
+}
+
+fn de<T>(encoded: Vec<u8>) -> Result<T, Error>
+where
+    T: DeserializeOwned + AvroSchema,
+{
+    assert!(!encoded.is_empty());
+    let schema = T::get_schema();
+    let mut reader = Reader::with_schema(&schema, &encoded[..])?;
+    if let Some(res) = reader.next() {
+        return res.and_then(|v| from_value::<T>(&v));
+    }
+    unreachable!("Nothing was encoded!")
+}
+
+mod container_attributes {
+    use super::*;
+
+    #[test]
+    fn avro_rs_xxx_rename() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        #[serde(rename = "Bar")]
+        struct Foo {
+            a: String,
+            b: i32,
+        }
+
+        let schema = r#"
+        {
+            "type":"record",
+            "name":"Foo",
+            "fields": [
+                {
+                    "name":"a",
+                    "type":"string"
+                },
+                {
+                    "name":"b",
+                    "type":"int"
+                }
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo {
+            a: "spam".to_string(),
+            b: 321,
+        });
+    }
+
+    #[test]
+    fn avro_rs_xxx_nested_rename() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        #[serde(rename = "Bar")]
+        struct Foo {
+            a: String,
+            b: i32,
+        }
+
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        struct Outer {
+            bar: Foo,
+        }
+
+        let schema = r#"
+        {
+            "type":"record",
+            "name":"Outer",
+            "fields": [
+                {
+                    "name":"bar",
+                    "type": {
+                        "type":"record",
+                        "name":"Foo",
+                        "fields": [
+                            {
+                                "name":"a",
+                                "type":"string"
+                            },
+                            {
+                                "name":"b",
+                                "type":"int"
+                            }
+                        ]
+                    }
+                }
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Outer::get_schema());
+
+        serde_assert(Outer {
+            bar: Foo {
+                a: "spam".to_string(),
+                b: 321,
+            },
+        });
+    }
+
+    #[test]
+    fn avro_rs_xxx_rename_all() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        #[serde(rename_all = "UPPERCASE")]
+        #[avro(rename_all = "UPPERCASE")]
+        struct Foo {
+            a: String,
+            b: i32,
+        }
+
+        let schema = r#"
+        {
+            "type":"record",
+            "name":"Foo",
+            "fields": [
+                {
+                    "name":"A",
+                    "type":"string"
+                },
+                {
+                    "name":"B",
+                    "type":"int"
+                }
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo {
+            a: "spam".to_string(),
+            b: 321,
+        });
+    }
+
+    #[test]
+    fn avro_rs_xxx_rename_all_no_avro() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        #[serde(rename_all = "UPPERCASE")]
+        struct Foo {
+            a: String,
+            b: i32,
+        }
+
+        let schema = r#"
+        {
+            "type":"record",
+            "name":"Foo",
+            "fields": [
+                {
+                    "name":"A",
+                    "type":"string"
+                },
+                {
+                    "name":"B",
+                    "type":"int"
+                }
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo {
+            a: "spam".to_string(),
+            b: 321,
+        });
+    }
+
+    #[test]
+    fn avro_rs_xxx_from_into() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        #[serde(from = "FooFromInto", into = "FooFromInto")]
+        struct Foo {
+            a: String,
+            b: i32,
+        }
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        struct FooFromInto {
+            a: String,
+            b: i32,
+        }
+
+        impl From<FooFromInto> for Foo {
+            fn from(value: FooFromInto) -> Self {
+                Self {
+                    a: value.a,
+                    b: value.b,
+                }
+            }
+        }
+
+        impl From<Foo> for FooFromInto {
+            fn from(value: Foo) -> Self {
+                Self {
+                    a: value.a,
+                    b: value.b,
+                }
+            }
+        }
+
+        let schema = r#"
+        {
+            "type":"record",
+            "name":"Foo",
+            "fields": [
+                {
+                    "name":"a",
+                    "type":"string"
+                },
+                {
+                    "name":"b",
+                    "type":"int"
+                }
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo {
+            a: "spam".to_string(),
+            b: 321,
+        });
+    }
+
+    #[test]
+    fn avro_rs_xxx_from_into_different() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        #[serde(from = "FooFromInto", into = "FooFromInto")]
+        struct Foo {
+            a: String,
+            b: i32,
+        }
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        struct FooFromInto {
+            a: String,
+            b: i32,
+            c: bool,
+        }
+
+        impl From<FooFromInto> for Foo {
+            fn from(value: FooFromInto) -> Self {
+                Self {
+                    a: value.a,
+                    b: value.b,
+                }
+            }
+        }
+
+        impl From<Foo> for FooFromInto {
+            fn from(value: Foo) -> Self {
+                Self {
+                    a: value.a,
+                    b: value.b,
+                    c: true,
+                }
+            }
+        }
+
+        let schema = r#"
+        {
+            "type":"record",
+            "name":"Foo",
+            "fields": [
+                {
+                    "name":"a",
+                    "type":"string"
+                },
+                {
+                    "name":"b",
+                    "type":"int"
+                }
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert_err(
+            Foo {
+                a: "spam".to_string(),
+                b: 321,
+            },
+            "Invalid field name c",
+        );
+    }
+}
+
+mod variant_attributes {
+    use super::*;
+
+    #[test]
+    fn avro_rs_xxx_rename_no_avro() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        enum Foo {
+            #[serde(rename = "Three")]
+            One,
+            Two,
+        }
+
+        let schema = r#"
+        {
+            "type":"enum",
+            "name":"Foo",
+            "symbols": [
+                "Three", "Two"
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo::One);
+    }
+
+    #[test]
+    fn avro_rs_xxx_rename() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        enum Foo {
+            #[serde(rename = "Three")]
+            #[avro(rename = "Three")]
+            One,
+            Two,
+        }
+
+        let schema = r#"
+        {
+            "type":"enum",
+            "name":"Foo",
+            "symbols": [
+                "Three", "Two"
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo::One);
+    }
+
+    #[test]
+    fn avro_rs_xxx_alias() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        enum Foo {
+            #[serde(rename = "Three", alias = "One")]
+            One,
+            Two,
+        }
+
+        let schema = r#"
+        {
+            "type":"enum",
+            "name":"Foo",
+            "symbols": [
+                "Three", "Two"
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo::One);
+    }
+
+    // This test does not build as avro-derive does not currently support enums with struct variants
+    // #[test]
+    // fn avro_rs_xxx_rename_all_fields() {
+    //     #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+    //     enum Foo {
+    //         #[serde(rename_all_fields = "UPPERCASE")]
+    //         #[avro(rename_all = "UPPERCASE")]
+    //         Bar {
+    //             a: String,
+    //             b: i32,
+    //         }
+    //     }
+
+    //     let schema = r#"
+    //     {
+    //         "type":"record",
+    //         "name":"Foo",
+    //         "fields": [
+    //             {
+    //                 "name":"A",
+    //                 "type":"string"
+    //             },
+    //             {
+    //                 "name":"B",
+    //                 "type":"int"
+    //             }
+    //         ]
+    //     }
+    //     "#;
+
+    //     let schema = Schema::parse_str(schema).unwrap();
+    //     assert_eq!(schema, Foo::get_schema());
+
+    //     serde_assert(Foo::Bar {
+    //         a: "spam".to_string(),
+    //         b: 321,
+    //     });
+    // }
+
+    #[test]
+    fn avro_rs_xxx_skip() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        enum Foo {
+            #[allow(dead_code)]
+            #[serde(skip)]
+            One,
+            Two,
+        }
+
+        let schema = r#"
+        {
+            "type":"enum",
+            "name":"Foo",
+            "symbols": [
+                "One", "Two"
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo::Two);
+    }
+}
+
+mod field_attributes {
+    use super::*;
+
+    #[test]
+    fn avro_rs_xxx_rename() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        struct Foo {
+            #[serde(rename = "c")]
+            #[avro(rename = "c")]
+            a: String,
+            b: i32,
+        }
+
+        let schema = r#"
+        {
+            "type":"record",
+            "name":"Foo",
+            "fields": [
+                {
+                    "name":"c",
+                    "type":"string"
+                },
+                {
+                    "name":"b",
+                    "type":"int"
+                }
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo {
+            a: "spam".to_string(),
+            b: 321,
+        });
+    }
+
+    #[test]
+    fn avro_rs_xxx_rename_no_avro() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        struct Foo {
+            #[serde(rename = "c")]
+            a: String,
+            b: i32,
+        }
+
+        let schema = r#"
+        {
+            "type":"record",
+            "name":"Foo",
+            "fields": [
+                {
+                    "name":"c",
+                    "type":"string"
+                },
+                {
+                    "name":"b",
+                    "type":"int"
+                }
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo {
+            a: "spam".to_string(),
+            b: 321,
+        });
+    }
+
+    #[test]
+    fn avro_rs_xxx_flatten() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        struct Nested {
+            a: bool,
+        }
+
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        struct Foo {
+            #[serde(flatten)]
+            #[avro(flatten)]
+            nested: Nested,
+            b: i32,
+        }
+
+        let schema = r#"
+        {
+            "type":"record",
+            "name":"Foo",
+            "fields": [
+                {
+                    "name":"a",
+                    "type":"boolean"
+                },
+                {
+                    "name":"b",
+                    "type":"int"
+                }
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo {
+            nested: Nested { a: true },
+            b: 321,
+        });
+    }
+
+    #[test]
+    fn avro_rs_xxx_flatten_no_avro() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        struct Nested {
+            a: bool,
+        }
+
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        struct Foo {
+            #[serde(flatten)]
+            nested: Nested,
+            b: i32,
+        }
+
+        let schema = r#"
+        {
+            "type":"record",
+            "name":"Foo",
+            "fields": [
+                {
+                    "name":"a",
+                    "type":"boolean"
+                },
+                {
+                    "name":"b",
+                    "type":"int"
+                }
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo {
+            nested: Nested { a: true },
+            b: 321,
+        });
+    }
+
+    #[test]
+    fn avro_rs_xxx_skip() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        struct Foo {
+            #[serde(skip)]
+            #[avro(skip)]
+            a: String,
+            b: i32,
+        }
+
+        let schema = r#"
+        {
+            "type":"record",
+            "name":"Foo",
+            "fields": [
+                {
+                    "name":"b",
+                    "type":"int"
+                }
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo {
+            a: "".to_string(),
+            b: 321,
+        });
+    }
+
+    #[test]
+    fn avro_rs_xxx_skip_no_avro() {
+        #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+        struct Foo {
+            #[serde(skip)]
+            a: String,
+            b: i32,
+        }
+
+        let schema = r#"
+        {
+            "type":"record",
+            "name":"Foo",
+            "fields": [
+                {
+                    "name":"b",
+                    "type":"int"
+                }
+            ]
+        }
+        "#;
+
+        let schema = Schema::parse_str(schema).unwrap();
+        assert_eq!(schema, Foo::get_schema());
+
+        serde_assert(Foo {
+            a: "".to_string(),
+            b: 321,
+        });
+    }
+}
diff --git a/avro_derive/tests/ui.rs b/avro_derive/tests/ui.rs
new file mode 100644
index 0000000..870c2f9
--- /dev/null
+++ b/avro_derive/tests/ui.rs
@@ -0,0 +1,5 @@
+#[test]
+fn ui() {
+    let t = trybuild::TestCases::new();
+    t.compile_fail("tests/ui/*.rs");
+}
diff --git a/avro_derive/tests/ui/avro_rs_226_skip_serializing.rs b/avro_derive/tests/ui/avro_rs_226_skip_serializing.rs
new file mode 100644
index 0000000..a065cdf
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_226_skip_serializing.rs
@@ -0,0 +1,11 @@
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+struct T {
+    x: Option<i8>,
+    y: Option<String>,
+    #[serde(skip_serializing)]
+    z: Option<i8>,
+}
+
+fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_226_skip_serializing.stderr b/avro_derive/tests/ui/avro_rs_226_skip_serializing.stderr
new file mode 100644
index 0000000..93557e4
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_226_skip_serializing.stderr
@@ -0,0 +1,5 @@
+error: `#[serde(skip_serializing)]` and `#[serde(skip_serializing_if)]` require `#[avro(default = "..")]
+ --> tests/ui/avro_rs_226_skip_serializing.rs:7:5
+  |
+7 |     #[serde(skip_serializing)]
+  |     ^
diff --git a/avro_derive/tests/ui/avro_rs_226_skip_serializing_if.rs b/avro_derive/tests/ui/avro_rs_226_skip_serializing_if.rs
new file mode 100644
index 0000000..f9bba75
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_226_skip_serializing_if.rs
@@ -0,0 +1,11 @@
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+struct T {
+    x: Option<i8>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    y: Option<String>,
+    z: Option<i8>,
+}
+
+fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_226_skip_serializing_if.stderr b/avro_derive/tests/ui/avro_rs_226_skip_serializing_if.stderr
new file mode 100644
index 0000000..c81af3a
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_226_skip_serializing_if.stderr
@@ -0,0 +1,5 @@
+error: `#[serde(skip_serializing)]` and `#[serde(skip_serializing_if)]` require `#[avro(default = "..")]
+ --> tests/ui/avro_rs_226_skip_serializing_if.rs:6:5
+  |
+6 |     #[serde(skip_serializing_if = "Option::is_none")]
+  |     ^
diff --git a/avro_derive/tests/ui/avro_rs_xxx_remote.rs b/avro_derive/tests/ui/avro_rs_xxx_remote.rs
new file mode 100644
index 0000000..ea4fa2f
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_remote.rs
@@ -0,0 +1,16 @@
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+struct Foo {
+    a: String,
+    b: i32,
+}
+
+#[derive(AvroSchema)]
+#[serde(remote = "Foo")]
+struct FooRemote {
+    a: String,
+    b: i32,
+}
+
+pub fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_xxx_remote.stderr b/avro_derive/tests/ui/avro_rs_xxx_remote.stderr
new file mode 100644
index 0000000..c60d8cc
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_remote.stderr
@@ -0,0 +1,5 @@
+error: AvroSchema derive does not support the Serde `remote` attribute
+  --> tests/ui/avro_rs_xxx_remote.rs:10:1
+   |
+10 | #[serde(remote = "Foo")]
+   | ^
diff --git a/avro_derive/tests/ui/avro_rs_xxx_rename_all_fields.rs b/avro_derive/tests/ui/avro_rs_xxx_rename_all_fields.rs
new file mode 100644
index 0000000..540f2d3
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_rename_all_fields.rs
@@ -0,0 +1,12 @@
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+#[serde(rename_all_fields = "UPPERCASE")]
+enum Foo {
+    Bar {
+        a: String,
+        b: i32,
+    }
+}
+
+pub fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_xxx_rename_all_fields.stderr b/avro_derive/tests/ui/avro_rs_xxx_rename_all_fields.stderr
new file mode 100644
index 0000000..d1ff21e
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_rename_all_fields.stderr
@@ -0,0 +1,5 @@
+error: AvroSchema derive does not work for enums with non unit structs
+ --> tests/ui/avro_rs_xxx_rename_all_fields.rs:5:6
+  |
+5 | enum Foo {
+  |      ^^^
diff --git a/avro_derive/tests/ui/avro_rs_xxx_skip_de_serializing.rs b/avro_derive/tests/ui/avro_rs_xxx_skip_de_serializing.rs
new file mode 100644
index 0000000..e95003a
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_skip_de_serializing.rs
@@ -0,0 +1,11 @@
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+struct T {
+    x: Option<i8>,
+    y: Option<String>,
+    #[serde(skip_serializing, skip_deserializing)]
+    z: Option<i8>,
+}
+
+fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_xxx_skip_de_serializing.stderr b/avro_derive/tests/ui/avro_rs_xxx_skip_de_serializing.stderr
new file mode 100644
index 0000000..ba1df1a
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_skip_de_serializing.stderr
@@ -0,0 +1,5 @@
+error: Use `#[serde(skip)]` instead of `#[serde(skip_serializing, skip_deserializing)]`
+ --> tests/ui/avro_rs_xxx_skip_de_serializing.rs:7:5
+  |
+7 |     #[serde(skip_serializing, skip_deserializing)]
+  |     ^
diff --git a/avro_derive/tests/ui/avro_rs_xxx_tag struct.rs b/avro_derive/tests/ui/avro_rs_xxx_tag struct.rs
new file mode 100644
index 0000000..1859d4f
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_tag struct.rs	
@@ -0,0 +1,10 @@
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+#[serde(tag = "bar")]
+struct Foo {
+    a: String,
+    b: i32,
+}
+
+pub fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_xxx_tag struct.stderr b/avro_derive/tests/ui/avro_rs_xxx_tag struct.stderr
new file mode 100644
index 0000000..cf2ebd6
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_tag struct.stderr	
@@ -0,0 +1,5 @@
+error: AvroSchema derive does not support changing the tagging Serde generates (`tag`, `content`, `untagged`, `variant_identifier`, `field_identifier`)
+ --> tests/ui/avro_rs_xxx_tag struct.rs:4:1
+  |
+4 | #[serde(tag = "bar")]
+  | ^
diff --git a/avro_derive/tests/ui/avro_rs_xxx_tag_content_enum.rs b/avro_derive/tests/ui/avro_rs_xxx_tag_content_enum.rs
new file mode 100644
index 0000000..df1cf3e
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_tag_content_enum.rs
@@ -0,0 +1,10 @@
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+#[serde(tag = "bar", content = "spam")]
+enum Foo {
+    One,
+    Two,
+}
+
+pub fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_xxx_tag_content_enum.stderr b/avro_derive/tests/ui/avro_rs_xxx_tag_content_enum.stderr
new file mode 100644
index 0000000..c1ca250
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_tag_content_enum.stderr
@@ -0,0 +1,5 @@
+error: AvroSchema derive does not support changing the tagging Serde generates (`tag`, `content`, `untagged`, `variant_identifier`, `field_identifier`)
+ --> tests/ui/avro_rs_xxx_tag_content_enum.rs:4:1
+  |
+4 | #[serde(tag = "bar", content = "spam")]
+  | ^
diff --git a/avro_derive/tests/ui/avro_rs_xxx_tag_enum.rs b/avro_derive/tests/ui/avro_rs_xxx_tag_enum.rs
new file mode 100644
index 0000000..d89bd09
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_tag_enum.rs
@@ -0,0 +1,10 @@
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+#[serde(tag = "bar")]
+enum Foo {
+    One,
+    Two,
+}
+
+pub fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_xxx_tag_enum.stderr b/avro_derive/tests/ui/avro_rs_xxx_tag_enum.stderr
new file mode 100644
index 0000000..9cbf28f
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_tag_enum.stderr
@@ -0,0 +1,5 @@
+error: AvroSchema derive does not support changing the tagging Serde generates (`tag`, `content`, `untagged`, `variant_identifier`, `field_identifier`)
+ --> tests/ui/avro_rs_xxx_tag_enum.rs:4:1
+  |
+4 | #[serde(tag = "bar")]
+  | ^
diff --git a/avro_derive/tests/ui/avro_rs_xxx_transparent.rs b/avro_derive/tests/ui/avro_rs_xxx_transparent.rs
new file mode 100644
index 0000000..0df31ef
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_transparent.rs
@@ -0,0 +1,15 @@
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+struct Foo {
+    a: String,
+    b: i32,
+}
+
+#[derive(AvroSchema)]
+#[serde(transparent)]
+struct Bar {
+    foo: Foo,
+}
+
+pub fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_xxx_transparent.stderr b/avro_derive/tests/ui/avro_rs_xxx_transparent.stderr
new file mode 100644
index 0000000..39b1e5f
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_transparent.stderr
@@ -0,0 +1,5 @@
+error: AvroSchema derive does not support Serde transparent
+  --> tests/ui/avro_rs_xxx_transparent.rs:10:1
+   |
+10 | #[serde(transparent)]
+   | ^
diff --git a/avro_derive/tests/ui/avro_rs_xxx_untagged_enum.rs b/avro_derive/tests/ui/avro_rs_xxx_untagged_enum.rs
new file mode 100644
index 0000000..29aa6e9
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_untagged_enum.rs
@@ -0,0 +1,10 @@
+use apache_avro::AvroSchema;
+
+#[derive(AvroSchema)]
+#[serde(untagged)]
+enum Foo {
+    One,
+    Two,
+}
+
+pub fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_xxx_untagged_enum.stderr b/avro_derive/tests/ui/avro_rs_xxx_untagged_enum.stderr
new file mode 100644
index 0000000..8b6df66
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_untagged_enum.stderr
@@ -0,0 +1,5 @@
+error: AvroSchema derive does not support changing the tagging Serde generates (`tag`, `content`, `untagged`, `variant_identifier`, `field_identifier`)
+ --> tests/ui/avro_rs_xxx_untagged_enum.rs:4:1
+  |
+4 | #[serde(untagged)]
+  | ^