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

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

commit da4ca3008e532eb4bfb2fd48579e292f0d03a401
Author: default <[email protected]>
AuthorDate: Tue Mar 17 16:05:18 2026 +0000

    wip: Update tests for `SchemaAwareSerializer`
---
 avro/src/bigdecimal.rs           |   45 +-
 avro/src/error.rs                |    2 +-
 avro/src/schema/mod.rs           |   64 ++
 avro/src/serde/derive.rs         |  384 ++++++++++--
 avro/src/serde/mod.rs            |    5 +-
 avro/src/serde/ser_schema/mod.rs | 1260 +++++++++++++++++---------------------
 avro/src/serde/with.rs           |  309 ++++++++++
 7 files changed, 1324 insertions(+), 745 deletions(-)

diff --git a/avro/src/bigdecimal.rs b/avro/src/bigdecimal.rs
index 7a6a379..ee26ee1 100644
--- a/avro/src/bigdecimal.rs
+++ b/avro/src/bigdecimal.rs
@@ -190,6 +190,47 @@ mod tests {
             big_decimal: BigDecimal,
         }
 
+        let schema_str = r#"
+        {
+          "type": "record",
+          "name": "Test",
+          "fields": [
+            {
+              "name": "big_decimal",
+              "type": "string"
+            }
+          ]
+        }
+        "#;
+        let schema = Schema::parse_str(schema_str)?;
+
+        let test = Test::default();
+
+        // write a record
+        let mut writer = Writer::new(&schema, Vec::new())?;
+        writer.append_ser(test.clone())?;
+
+        let wrote_data = writer.into_inner()?;
+
+        // read record
+        let mut reader = Reader::new(&wrote_data[..])?;
+
+        let value = reader.next().unwrap()?;
+
+        assert_eq!(test, from_value::<Test>(&value)?);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_338_deserialize_serde_way_with_bigdecimal() -> TestResult {
+        #[derive(Clone, PartialEq, Eq, Debug, Default, serde::Deserialize, 
serde::Serialize)]
+        #[serde(rename = "test")]
+        struct Test {
+            #[serde(with = "crate::serde::bigdecimal")]
+            big_decimal: BigDecimal,
+        }
+
         let schema_str = r#"
         {
           "type": "record",
@@ -216,11 +257,11 @@ mod tests {
         let wrote_data = writer.into_inner()?;
 
         // read record
-        let mut reader = Reader::new(&wrote_data[..])?;
+        let mut reader = Reader::new(&wrote_data[..])?.into_deser_iter();
 
         let value = reader.next().unwrap()?;
 
-        assert_eq!(test, from_value::<Test>(&value)?);
+        assert_eq!(test, value);
 
         Ok(())
     }
diff --git a/avro/src/error.rs b/avro/src/error.rs
index 85c7fd8..750bafc 100644
--- a/avro/src/error.rs
+++ b/avro/src/error.rs
@@ -559,7 +559,7 @@ pub enum Details {
     #[error("Failed to serialize value into Avro value: {0}")]
     SerializeValue(String),
 
-    #[error("Failed to serialize value of type {value_type} using schema 
{schema:?}: {value}")]
+    #[error("Failed to serialize value of type `{value_type}` using 
Schema::{schema:?}: {value}")]
     SerializeValueWithSchema {
         value_type: &'static str,
         value: String,
diff --git a/avro/src/schema/mod.rs b/avro/src/schema/mod.rs
index 40476cb..ccaa9f8 100644
--- a/avro/src/schema/mod.rs
+++ b/avro/src/schema/mod.rs
@@ -45,6 +45,7 @@ use serde::{
     ser::{SerializeMap, SerializeSeq},
 };
 use serde_json::{Map, Value as JsonValue};
+use std::borrow::Cow;
 use std::fmt::Formatter;
 use std::{
     collections::{BTreeMap, HashMap, HashSet},
@@ -784,6 +785,69 @@ impl Schema {
         }
         Ok(())
     }
+
+    /// Derive a name for this schema.
+    ///
+    /// The name is a valid schema name and will be unique if the named
+    /// schemas in this schema have unique names.
+    pub(crate) fn unique_normalized_name(&self) -> Cow<'static, str> {
+        match self {
+            Schema::Null => Cow::Borrowed("n"),
+            Schema::Boolean => Cow::Borrowed("B"),
+            Schema::Int => Cow::Borrowed("i"),
+            Schema::Long => Cow::Borrowed("l"),
+            Schema::Float => Cow::Borrowed("f"),
+            Schema::Double => Cow::Borrowed("d"),
+            Schema::Bytes => Cow::Borrowed("b"),
+            Schema::String => Cow::Borrowed("s"),
+            Schema::Array(array) => {
+                Cow::Owned(format!("a_{}", 
array.items.unique_normalized_name()))
+            }
+            Schema::Map(map) => Cow::Owned(format!("m_{}", 
map.types.unique_normalized_name())),
+            Schema::Union(union) => {
+                let mut name = format!("u{}", union.schemas.len());
+                for schema in &union.schemas {
+                    name.push('_');
+                    name.push_str(&schema.unique_normalized_name());
+                }
+                Cow::Owned(name)
+            }
+            Schema::BigDecimal => Cow::Borrowed("bd"),
+            Schema::Date => Cow::Borrowed("D"),
+            Schema::TimeMillis => Cow::Borrowed("t"),
+            Schema::TimeMicros => Cow::Borrowed("tm"),
+            Schema::TimestampMillis => Cow::Borrowed("T"),
+            Schema::TimestampMicros => Cow::Borrowed("TM"),
+            Schema::TimestampNanos => Cow::Borrowed("TN"),
+            Schema::LocalTimestampMillis => Cow::Borrowed("L"),
+            Schema::LocalTimestampMicros => Cow::Borrowed("LM"),
+            Schema::LocalTimestampNanos => Cow::Borrowed("LN"),
+            Schema::Decimal(DecimalSchema {
+                inner: InnerDecimalSchema::Bytes,
+                precision,
+                scale,
+            }) => Cow::Owned(format!("db_{precision}_{scale}")),
+            Schema::Uuid(UuidSchema::Bytes) => Cow::Borrowed("ub"),
+            Schema::Uuid(UuidSchema::String) => Cow::Borrowed("us"),
+            Schema::Record(RecordSchema { name, .. })
+            | Schema::Enum(EnumSchema { name, .. })
+            | Schema::Fixed(FixedSchema { name, .. })
+            | Schema::Decimal(DecimalSchema {
+                inner: InnerDecimalSchema::Fixed(FixedSchema { name, .. }),
+                ..
+            })
+            | Schema::Uuid(UuidSchema::Fixed(FixedSchema { name, .. }))
+            | Schema::Duration(FixedSchema { name, .. })
+            | Schema::Ref { name } => {
+                let name: String = name
+                    .to_string()
+                    .chars()
+                    .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
+                    .collect();
+                Cow::Owned(format!("r{}_{}", name.len(), name))
+            }
+        }
+    }
 }
 
 impl Serialize for Schema {
diff --git a/avro/src/serde/derive.rs b/avro/src/serde/derive.rs
index 33c4f5b..c050814 100644
--- a/avro/src/serde/derive.rs
+++ b/avro/src/serde/derive.rs
@@ -558,27 +558,6 @@ macro_rules! impl_array_schema (
 
 impl_array_schema!([T] where T: AvroSchemaComponent);
 impl_array_schema!(Vec<T> where T: AvroSchemaComponent);
-// This doesn't work as the macro doesn't allow specifying the N parameter
-// impl_array_schema!([T; N] where T: AvroSchemaComponent);
-
-impl<const N: usize, T> AvroSchemaComponent for [T; N]
-where
-    T: AvroSchemaComponent,
-{
-    fn get_schema_in_ctxt(
-        named_schemas: &mut HashSet<Name>,
-        enclosing_namespace: NamespaceRef,
-    ) -> Schema {
-        Schema::array(T::get_schema_in_ctxt(named_schemas, 
enclosing_namespace)).build()
-    }
-
-    fn get_record_fields_in_ctxt(
-        _: &mut HashSet<Name>,
-        _: NamespaceRef,
-    ) -> Option<Vec<RecordField>> {
-        None
-    }
-}
 
 impl<T> AvroSchemaComponent for HashMap<String, T>
 where
@@ -630,27 +609,35 @@ where
 }
 
 impl AvroSchemaComponent for core::time::Duration {
-    /// The schema is [`Schema::Duration`] with the name `duration`.
+    /// The schema is [`Schema::Record`] with the name `Duration`.
     ///
-    /// This is a lossy conversion as this Avro type does not store the amount 
of nanoseconds.
+    /// It has two fields:
+    /// - `secs` with the schema `Schema::Fixed(name: "u64", size: 8)`
+    /// - `nanos` with the schema `Schema::Long`
     fn get_schema_in_ctxt(
         named_schemas: &mut HashSet<Name>,
         enclosing_namespace: NamespaceRef,
     ) -> Schema {
-        let name = Name::new_with_enclosing_namespace("duration", 
enclosing_namespace)
+        let name = Name::new_with_enclosing_namespace("Duration", 
enclosing_namespace)
             .expect("Name is valid");
         if named_schemas.contains(&name) {
             Schema::Ref { name }
         } else {
-            let schema = Schema::Duration(FixedSchema {
-                name: name.clone(),
-                aliases: None,
-                doc: None,
-                size: 12,
-                attributes: Default::default(),
-            });
-            named_schemas.insert(name);
-            schema
+            named_schemas.insert(name.clone());
+            Schema::record(name)
+                .fields(vec![
+                    // Secs is an u64
+                    RecordField::builder()
+                        .name("secs")
+                        .schema(u64::get_schema_in_ctxt(named_schemas, 
enclosing_namespace))
+                        .build(),
+                    // Nanos is an u32
+                    RecordField::builder()
+                        .name("nanos")
+                        .schema(Schema::Long)
+                        .build(),
+                ])
+                .build()
         }
     }
 
@@ -666,6 +653,8 @@ impl AvroSchemaComponent for uuid::Uuid {
     /// The schema is [`Schema::Uuid`] with the name `uuid`.
     ///
     /// The underlying schema is [`Schema::Fixed`] with a size of 16.
+    ///
+    /// If you're using `human_readable: true` you need to override this 
schema with a `Schema::String`.
     fn get_schema_in_ctxt(
         named_schemas: &mut HashSet<Name>,
         enclosing_namespace: NamespaceRef,
@@ -788,13 +777,197 @@ impl AvroSchemaComponent for i128 {
     }
 }
 
+/// Schema definition for `[T; N]`
+///
+/// Schema is defined as follows:
+/// - 0-sized arrays: [`Schema::Null`]
+/// - 1-sized arrays: `T::get_schema_in_ctxt`
+/// - N-sized arrays: [`Schema::Record`] with a field for every index
+///
+/// If you need or want a [`Schema::Array`], [`Schema::Bytes`], or 
[`Schema::Fixed`] instead,
+/// use [`apache_avro::serde::array`], [`apache_avro::serde::bytes`], or 
[`apache_avro::serde::fixed`] respectively.
+///
+/// [`apache_avro::serde::array`]: crate::serde::array
+/// [`apache_avro::serde::bytes`]: crate::serde::bytes
+/// [`apache_avro::serde::fixed`]: crate::serde::fixed
+impl<const N: usize, T: AvroSchemaComponent> AvroSchemaComponent for [T; N] {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut HashSet<Name>,
+        enclosing_namespace: NamespaceRef,
+    ) -> Schema {
+        if N == 0 {
+            Schema::Null
+        } else if N == 1 {
+            T::get_schema_in_ctxt(named_schemas, enclosing_namespace)
+        } else {
+            let t_schema = T::get_schema_in_ctxt(named_schemas, 
enclosing_namespace);
+            let name = Name::new_with_enclosing_namespace(
+                format!("A{N}_{}", t_schema.unique_normalized_name()),
+                enclosing_namespace,
+            )
+            .expect("Name is valid");
+            if named_schemas.contains(&name) {
+                Schema::Ref { name }
+            } else {
+                named_schemas.insert(name.clone());
+
+                let t_default = T::field_default();
+                // If T is a named schema or contains named schemas, they'll 
now be a reference.
+                let t_ref = T::get_schema_in_ctxt(named_schemas, 
enclosing_namespace);
+                let fields = std::iter::once(
+                    RecordField::builder()
+                        .name("field_0".to_string())
+                        .schema(t_schema)
+                        .maybe_default(t_default.clone())
+                        .build(),
+                )
+                .chain((1..N).map(|n| {
+                    RecordField::builder()
+                        .name(format!("field_{n}"))
+                        .schema(t_ref.clone())
+                        .maybe_default(t_default.clone())
+                        .build()
+                }))
+                .collect();
+
+                Schema::record(name).fields(fields).build()
+            }
+        }
+    }
+
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut HashSet<Name>,
+        enclosing_namespace: NamespaceRef,
+    ) -> Option<Vec<RecordField>> {
+        if N == 0 {
+            None
+        } else if N == 1 {
+            T::get_record_fields_in_ctxt(named_schemas, enclosing_namespace)
+        } else {
+            let t_schema = T::get_schema_in_ctxt(named_schemas, 
enclosing_namespace);
+            let t_default = T::field_default();
+            // If T is a named schema or contains named schemas, they'll now 
be a reference.
+            let t_ref = T::get_schema_in_ctxt(named_schemas, 
enclosing_namespace);
+            let fields = std::iter::once(
+                RecordField::builder()
+                    .name("field_0".to_string())
+                    .schema(t_schema)
+                    .maybe_default(t_default.clone())
+                    .build(),
+            )
+            .chain((1..N).map(|n| {
+                RecordField::builder()
+                    .name(format!("field_{n}"))
+                    .schema(t_ref.clone())
+                    .maybe_default(t_default.clone())
+                    .build()
+            }))
+            .collect();
+            Some(fields)
+        }
+    }
+
+    /// `None` for 0-sized and N-sized arrays, `T::field_default` for 1-sized 
arrays
+    fn field_default() -> Option<serde_json::Value> {
+        if N == 1 { T::field_default() } else { None }
+    }
+}
+
+/// Schema definition for `(T₁, T₂, …, Tₙ)`.
+///
+/// Implemented for tuples of up to 16 elements.
+///
+/// Schema is defined as follows:
+/// - 1-tuple: `T::get_schema_in_ctxt`
+/// - N-tuple: [`Schema::Record`] with a field for every element
+#[cfg_attr(docsrs, doc(fake_variadic))]
+impl<T: AvroSchemaComponent> AvroSchemaComponent for (T,) {
+    fn get_schema_in_ctxt(
+        named_schemas: &mut HashSet<Name>,
+        enclosing_namespace: NamespaceRef,
+    ) -> Schema {
+        T::get_schema_in_ctxt(named_schemas, enclosing_namespace)
+    }
+
+    fn get_record_fields_in_ctxt(
+        named_schemas: &mut HashSet<Name>,
+        enclosing_namespace: NamespaceRef,
+    ) -> Option<Vec<RecordField>> {
+        T::get_record_fields_in_ctxt(named_schemas, enclosing_namespace)
+    }
+
+    /// `None` for N-tuples, `T::field_default()` for 1-tuple.
+    fn field_default() -> Option<serde_json::Value> {
+        T::field_default()
+    }
+}
+
+macro_rules! tuple_impls {
+    ($($len:expr => ($($name:ident)+))+) => {
+        $(
+            #[cfg_attr(docsrs, doc(hidden))]
+            impl<$($name: AvroSchemaComponent),+> AvroSchemaComponent for 
($($name),+) {
+                fn get_schema_in_ctxt(named_schemas: &mut HashSet<Name>, 
enclosing_namespace: NamespaceRef) -> Schema {
+                    let schemas: [Schema; $len] = 
[$($name::get_schema_in_ctxt(named_schemas, enclosing_namespace)),+];
+
+                    let mut name = format!("T{}", $len);
+                    for schema in &schemas {
+                        name.push('_');
+                        name.push_str(&schema.unique_normalized_name());
+                    }
+                    let name = Name::new_with_enclosing_namespace(name, 
enclosing_namespace).expect("Name is valid");
+
+                    if named_schemas.contains(&name) {
+                        Schema::Ref { name }
+                    } else {
+                        named_schemas.insert(name.clone());
+
+                        let defaults: [Option<serde_json::Value>; $len] = 
[$($name::field_default()),+];
+
+                        let fields = 
schemas.into_iter().zip(defaults.into_iter()).enumerate().map(|(n, (schema, 
default))| {
+                            RecordField::builder()
+                                .name(format!("field_{n}"))
+                                .schema(schema)
+                                .maybe_default(default)
+                                .build()
+                        }).collect();
+
+                        Schema::record(name).fields(fields).build()
+                    }
+                }
+            }
+        )+
+    }
+}
+
+tuple_impls! {
+    2 => (T0 T1)
+    3 => (T0 T1 T2)
+    4 => (T0 T1 T2 T3)
+    5 => (T0 T1 T2 T3 T4)
+    6 => (T0 T1 T2 T3 T4 T5)
+    7 => (T0 T1 T2 T3 T4 T5 T6)
+    8 => (T0 T1 T2 T3 T4 T5 T6 T7)
+    9 => (T0 T1 T2 T3 T4 T5 T6 T7 T8)
+    10 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9)
+    11 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10)
+    12 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11)
+    13 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12)
+    14 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13)
+    15 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14)
+    16 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15)
+}
+
 #[cfg(test)]
 mod tests {
+    use crate::reader::datum::GenericDatumReader;
+    use crate::writer::datum::GenericDatumWriter;
     use crate::{
         AvroSchema, Schema,
         schema::{FixedSchema, Name},
     };
     use apache_avro_test_helper::TestResult;
+    use std::io::Cursor;
 
     #[test]
     fn avro_rs_401_str() -> TestResult {
@@ -823,23 +996,12 @@ mod tests {
         Ok(())
     }
 
-    #[test]
-    fn avro_rs_401_array() -> TestResult {
-        let schema = <[u8; 55]>::get_schema();
-        assert_eq!(schema, Schema::array(Schema::Int).build());
-
-        Ok(())
-    }
-
     #[test]
     fn avro_rs_401_option_ref_slice_array() -> TestResult {
-        let schema = <Option<&[[u8; 55]]>>::get_schema();
+        let schema = <Option<&[u8]>>::get_schema();
         assert_eq!(
             schema,
-            Schema::union(vec![
-                Schema::Null,
-                Schema::array(Schema::array(Schema::Int).build()).build()
-            ])?
+            Schema::union(vec![Schema::Null, 
Schema::array(Schema::Int).build()])?
         );
 
         Ok(())
@@ -927,4 +1089,134 @@ mod tests {
     fn avro_rs_489_option_option() {
         <Option<Option<i32>>>::get_schema();
     }
+
+    #[test]
+    fn avro_rs_xxx_std_time_duration() -> TestResult {
+        let schema = Schema::parse_str(
+            r#"{
+            "type": "record",
+            "name": "Duration",
+            "fields": [
+                { "name": "secs", "type": {"type": "fixed", "name": "u64", 
"size": 8} },
+                { "name": "nanos", "type": "long" }
+            ]
+        }"#,
+        )?;
+        let zero = std::time::Duration::ZERO;
+        let max = std::time::Duration::MAX;
+        assert_eq!(schema, std::time::Duration::get_schema());
+
+        let writer = GenericDatumWriter::builder(&schema).build()?;
+        let written_zero = writer.write_ser_to_vec(&zero)?;
+        let written_max = writer.write_ser_to_vec(&max)?;
+
+        let reader = GenericDatumReader::builder(&schema).build()?;
+        let read_zero = reader.read_deser(&mut Cursor::new(written_zero))?;
+        assert_eq!(zero, read_zero);
+        let read_max = reader.read_deser(&mut Cursor::new(written_max))?;
+        assert_eq!(max, read_max);
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_0_array() -> TestResult {
+        assert_eq!(Schema::Null, <[String; 0]>::get_schema());
+        assert_eq!(Schema::Null, <[(); 0]>::get_schema());
+        assert_eq!(Schema::Null, <[bool; 0]>::get_schema());
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_1_array() -> TestResult {
+        assert_eq!(Schema::String, <[String; 1]>::get_schema());
+        assert_eq!(Schema::Null, <[(); 1]>::get_schema());
+        assert_eq!(Schema::Boolean, <[bool; 1]>::get_schema());
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_n_array() -> TestResult {
+        let schema = Schema::parse_str(
+            r#"{
+            "type": "record",
+            "name": "A5_s",
+            "fields": [
+                { "name": "field_0", "type": "string" },
+                { "name": "field_1", "type": "string" },
+                { "name": "field_2", "type": "string" },
+                { "name": "field_3", "type": "string" },
+                { "name": "field_4", "type": "string" }
+            ]
+        }"#,
+        )?;
+
+        assert_eq!(schema, <[String; 5]>::get_schema());
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_n_array_complex_type() -> TestResult {
+        let schema = Schema::parse_str(
+            r#"{
+            "type": "record",
+            "name": "A2_u2_n_r4_uuid",
+            "fields": [
+                { "name": "field_0", "type": ["null", {"type": "fixed", 
"logicalType": "uuid", "size": 16, "name": "uuid"}], "default": null },
+                { "name": "field_1", "type": ["null", "uuid"], "default": null 
}
+            ]
+        }"#,
+        )?;
+
+        assert_eq!(schema, <[Option<uuid::Uuid>; 2]>::get_schema());
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_1_tuple() -> TestResult {
+        assert_eq!(Schema::String, <(String,)>::get_schema());
+        assert_eq!(Schema::Null, <((),)>::get_schema());
+        assert_eq!(Schema::Boolean, <(bool,)>::get_schema());
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_n_tuple() -> TestResult {
+        let schema = Schema::parse_str(
+            r#"{
+            "type": "record",
+            "name": "T5_s_i_l_B_n",
+            "fields": [
+                { "name": "field_0", "type": "string" },
+                { "name": "field_1", "type": "int" },
+                { "name": "field_2", "type": "long" },
+                { "name": "field_3", "type": "boolean" },
+                { "name": "field_4", "type": "null" }
+            ]
+        }"#,
+        )?;
+
+        assert_eq!(schema, <(String, i32, i64, bool, ())>::get_schema());
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rx_xxx_n_tuple_complex_type() -> TestResult {
+        let schema = Schema::parse_str(
+            r#"{
+            "type": "record",
+            "name": "A2_u2_n_r4_uuid",
+            "fields": [
+                { "name": "field_0", "type": ["null", {"type": "fixed", 
"logicalType": "uuid", "size": 16, "name": "uuid"}], "default": "null" },
+                { "name": "field_1", "type": "uuid" },
+                { "name": "field_2", "type": "string" }
+            ]
+        }"#,
+        )?;
+
+        assert_eq!(
+            schema,
+            <(Option<uuid::Uuid>, uuid::Uuid, String)>::get_schema()
+        );
+        Ok(())
+    }
 }
diff --git a/avro/src/serde/mod.rs b/avro/src/serde/mod.rs
index 256963d..3b1718b 100644
--- a/avro/src/serde/mod.rs
+++ b/avro/src/serde/mod.rs
@@ -118,7 +118,10 @@ mod with;
 pub use de::from_value;
 pub use derive::{AvroSchema, AvroSchemaComponent};
 pub use ser::to_value;
-pub use with::{bytes, bytes_opt, fixed, fixed_opt, slice, slice_opt};
+pub use with::{
+    array, array_opt, bigdecimal, bigdecimal_opt, bytes, bytes_opt, fixed, 
fixed_opt, slice,
+    slice_opt,
+};
 
 #[doc(hidden)]
 pub use derive::get_record_fields_in_ctxt;
diff --git a/avro/src/serde/ser_schema/mod.rs b/avro/src/serde/ser_schema/mod.rs
index 829cec4..0c2bc9c 100644
--- a/avro/src/serde/ser_schema/mod.rs
+++ b/avro/src/serde/ser_schema/mod.rs
@@ -268,7 +268,7 @@ impl<'s, 'w, W: Write, S: Borrow<Schema>> Serializer for 
SchemaAwareSerializer<'
     }
 
     fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
-        self.checked_write_long("i64", i64::from(v))
+        self.checked_write_long("u32", i64::from(v))
     }
 
     fn serialize_u64(mut self, v: u64) -> Result<Self::Ok, Self::Error> {
@@ -334,7 +334,7 @@ impl<'s, 'w, W: Write, S: Borrow<Schema>> Serializer for 
SchemaAwareSerializer<'
             Schema::Union(union) => {
                 UnionSerializer::new(self.writer, union, 
self.config).serialize_str(v)
             }
-            _ => Err(self.error("string", "Expected Schema::String ")),
+            _ => Err(self.error("str", "Expected Schema::String | 
Schema::Uuid(String)")),
         }
     }
 
@@ -406,7 +406,7 @@ impl<'s, 'w, W: Write, S: Borrow<Schema>> Serializer for 
SchemaAwareSerializer<'
             }
             _ => Err(self.error(
                 "unit struct",
-                format!("Expected Schema::Record(name: {name}, fields: [])"),
+                format!(r#"Expected Schema::Record(name: "{name}", fields: 
[])"#),
             )),
         }
     }
@@ -527,7 +527,10 @@ impl<'s, 'w, W: Write, S: Borrow<Schema>> Serializer for 
SchemaAwareSerializer<'
                 None,
             )),
             // This error case can only happen for len > 1
-            _ => Err(self.error("tuple", "Expected Schema::Record(fields.len() 
== {len}")),
+            _ => Err(self.error(
+                "tuple",
+                format!("Expected Schema::Record(fields.len() == {len})"),
+            )),
         }
     }
 
@@ -724,7 +727,7 @@ impl<'s, 'w, W: Write, S: Borrow<Schema>> SerializeMap for 
MapOrRecordSerializer
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::schema::FixedSchema;
+    use crate::schema::{FixedSchema, Names};
     use crate::{
         Days, Duration, Millis, Months, Reader, Writer, decimal::Decimal, 
error::Details,
         from_value, schema::ResolvedSchema,
@@ -732,40 +735,89 @@ mod tests {
     use apache_avro_test_helper::TestResult;
     use bigdecimal::BigDecimal;
     use num_bigint::{BigInt, Sign};
+    use pretty_assertions::assert_eq;
     use serde::{Deserialize, Serialize};
-    use serde_bytes::{ByteArray, Bytes};
+    use serde_bytes::Bytes;
     use std::{
         collections::{BTreeMap, HashMap},
         marker::PhantomData,
     };
     use uuid::Uuid;
 
+    #[track_caller]
+    fn assert_serialize_err<T: Serialize, S: Borrow<Schema>>(
+        t: T,
+        schema: &Schema,
+        names: &HashMap<Name, S>,
+        expected: &str,
+    ) {
+        let config = Config {
+            names,
+            target_block_size: None,
+            human_readable: false,
+        };
+        let mut buffer = Vec::new();
+        let serializer = SchemaAwareSerializer::new(&mut buffer, &schema, 
config).unwrap();
+        let error = t
+            .serialize(serializer)
+            .expect_err("This should not serialize");
+        assert_eq!(error.to_string(), expected);
+    }
+
+    #[track_caller]
+    fn assert_serialize<T: Serialize, S: Borrow<Schema>>(
+        t: T,
+        schema: &Schema,
+        names: &HashMap<Name, S>,
+        expected: &[u8],
+    ) {
+        let config = Config {
+            names,
+            target_block_size: None,
+            human_readable: false,
+        };
+        let mut buffer = Vec::new();
+        let serializer = SchemaAwareSerializer::new(&mut buffer, &schema, 
config).unwrap();
+        let _bytes_written = t.serialize(serializer).expect("This should 
serialize");
+        assert_eq!(&buffer, expected);
+    }
+
     #[test]
     fn test_serialize_null() -> TestResult {
         let schema = Schema::Null;
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        ().serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        None::<()>.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        None::<i32>.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        None::<String>.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
-        assert!(
-            "".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize((), &schema, &names, &[]);
+        assert_serialize_err(
+            None::<()>,
+            &schema,
+            &names,
+            "Failed to serialize value of type `none` using Schema::Null: 
Expected Schema::Union([Schema::Null, _])",
         );
-        assert!(
-            Some("")
-                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize_err(
+            None::<i32>,
+            &schema,
+            &names,
+            "Failed to serialize value of type `none` using Schema::Null: 
Expected Schema::Union([Schema::Null, _])",
+        );
+        assert_serialize_err(
+            None::<String>,
+            &schema,
+            &names,
+            "Failed to serialize value of type `none` using Schema::Null: 
Expected Schema::Union([Schema::Null, _])",
+        );
+        assert_serialize_err(
+            "",
+            &schema,
+            &names,
+            "Failed to serialize value of type `str` using Schema::Null: 
Expected Schema::String | Schema::Uuid(String)",
+        );
+        assert_serialize_err(
+            Some(""),
+            &schema,
+            &names,
+            "Failed to serialize value of type `some` using Schema::Null: 
Expected Schema::Union([Schema::Null, _])",
         );
-
-        assert_eq!(buffer.as_slice(), Vec::<u8>::new().as_slice());
 
         Ok(())
     }
@@ -773,59 +825,54 @@ mod tests {
     #[test]
     fn test_serialize_bool() -> TestResult {
         let schema = Schema::Boolean;
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        true.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        false.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        assert!(
-            "".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize(true, &schema, &names, &[1]);
+        assert_serialize(false, &schema, &names, &[0]);
+        assert_serialize_err(
+            "",
+            &schema,
+            &names,
+            "Failed to serialize value of type `str` using Schema::Boolean: 
Expected Schema::String | Schema::Uuid(String)",
         );
-        assert!(
-            Some("")
-                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize_err(
+            Some(""),
+            &schema,
+            &names,
+            "Failed to serialize value of type `some` using Schema::Boolean: 
Expected Schema::Union([Schema::Null, _])",
         );
 
-        assert_eq!(buffer.as_slice(), &[1, 0]);
-
         Ok(())
     }
 
     #[test]
     fn test_serialize_int() -> TestResult {
         let schema = Schema::Int;
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        4u8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        31u16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        13u32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        7i8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        (-57i16).serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        129i32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        assert!(
-            "".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
-        );
-        assert!(
-            Some("")
-                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize(4u8, &schema, &names, &[8]);
+        assert_serialize(31u16, &schema, &names, &[62]);
+        assert_serialize(7i8, &schema, &names, &[14]);
+        assert_serialize(-57i16, &schema, &names, &[113]);
+        assert_serialize(129i32, &schema, &names, &[130, 2]);
+        assert_serialize_err(
+            13u32,
+            &schema,
+            &names,
+            "Failed to serialize value of type `u32` using Schema::Int: 
Expected Schema::Long | Schema::TimeMicros | 
Schema::{,Local}Timestamp{Millis,Micros,Nanos}",
+        );
+        assert_serialize_err(
+            "",
+            &schema,
+            &names,
+            "Failed to serialize value of type `str` using Schema::Int: 
Expected Schema::String | Schema::Uuid(String)",
+        );
+        assert_serialize_err(
+            Some(""),
+            &schema,
+            &names,
+            "Failed to serialize value of type `some` using Schema::Int: 
Expected Schema::Union([Schema::Null, _])",
         );
-
-        assert_eq!(buffer.as_slice(), &[8, 62, 26, 14, 113, 130, 2]);
 
         Ok(())
     }
@@ -833,35 +880,57 @@ mod tests {
     #[test]
     fn test_serialize_long() -> TestResult {
         let schema = Schema::Long;
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        4u8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        31u16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        13u32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        291u64.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        7i8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        (-57i16).serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        129i32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        (-432i64).serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        assert!(
-            "".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
-        );
-        assert!(
-            Some("")
-                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize(13u32, &schema, &names, &[26]);
+        assert_serialize(-432i64, &schema, &names, &[223, 6]);
+        assert_serialize_err(
+            4u8,
+            &schema,
+            &names,
+            "Failed to serialize value of type `u8` using Schema::Long: 
Expected Schema::Int | Schema::Date | Schema::TimeMillis",
         );
-
-        assert_eq!(
-            buffer.as_slice(),
-            &[8, 62, 26, 198, 4, 14, 113, 130, 2, 223, 6]
+        assert_serialize_err(
+            31u16,
+            &schema,
+            &names,
+            "Failed to serialize value of type `u16` using Schema::Long: 
Expected Schema::Int | Schema::Date | Schema::TimeMillis",
+        );
+        assert_serialize_err(
+            7i8,
+            &schema,
+            &names,
+            "Failed to serialize value of type `i8` using Schema::Long: 
Expected Schema::Int | Schema::Date | Schema::TimeMillis",
+        );
+        assert_serialize_err(
+            -57i16,
+            &schema,
+            &names,
+            "Failed to serialize value of type `i16` using Schema::Long: 
Expected Schema::Int | Schema::Date | Schema::TimeMillis",
+        );
+        assert_serialize_err(
+            129i32,
+            &schema,
+            &names,
+            "Failed to serialize value of type `i32` using Schema::Long: 
Expected Schema::Int | Schema::Date | Schema::TimeMillis",
+        );
+        assert_serialize_err(
+            24u64,
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `u64` using Schema::Long: 
Expected Schema::Fixed(name: "u64", size: 8)"#,
+        );
+        assert_serialize_err(
+            "",
+            &schema,
+            &names,
+            "Failed to serialize value of type `str` using Schema::Long: 
Expected Schema::String | Schema::Uuid(String)",
+        );
+        assert_serialize_err(
+            Some(""),
+            &schema,
+            &names,
+            "Failed to serialize value of type `some` using Schema::Long: 
Expected Schema::Union([Schema::Null, _])",
         );
 
         Ok(())
@@ -870,55 +939,60 @@ mod tests {
     #[test]
     fn test_serialize_float() -> TestResult {
         let schema = Schema::Float;
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        4.7f32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        (-14.1f64).serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        assert!(
-            "".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize(4.7f32, &schema, &names, &[102, 102, 150, 64]);
+        assert_serialize_err(
+            -14.1f64,
+            &schema,
+            &names,
+            "Failed to serialize value of type `f64` using Schema::Float: 
Expected Schema::Double",
         );
-        assert!(
-            Some("")
-                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize_err(
+            "",
+            &schema,
+            &names,
+            "Failed to serialize value of type `str` using Schema::Float: 
Expected Schema::String | Schema::Uuid(String)",
+        );
+        assert_serialize_err(
+            Some(""),
+            &schema,
+            &names,
+            "Failed to serialize value of type `some` using Schema::Float: 
Expected Schema::Union([Schema::Null, _])",
         );
-
-        assert_eq!(buffer.as_slice(), &[102, 102, 150, 64, 154, 153, 97, 193]);
 
         Ok(())
     }
 
     #[test]
     fn test_serialize_double() -> TestResult {
-        let schema = Schema::Float;
-        let mut buffer: Vec<u8> = Vec::new();
+        let schema = Schema::Double;
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        4.7f32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        (-14.1f64).serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        assert!(
-            "".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize(
+            -14.1f64,
+            &schema,
+            &names,
+            &[51, 51, 51, 51, 51, 51, 44, 192],
         );
-        assert!(
-            Some("")
-                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize_err(
+            4.7f32,
+            &schema,
+            &names,
+            "Failed to serialize value of type `f32` using Schema::Double: 
Expected Schema::Float",
+        );
+        assert_serialize_err(
+            "",
+            &schema,
+            &names,
+            "Failed to serialize value of type `str` using Schema::Double: 
Expected Schema::String | Schema::Uuid(String)",
+        );
+        assert_serialize_err(
+            Some(""),
+            &schema,
+            &names,
+            "Failed to serialize value of type `some` using Schema::Double: 
Expected Schema::Union([Schema::Null, _])",
         );
-
-        assert_eq!(buffer.as_slice(), &[102, 102, 150, 64, 154, 153, 97, 193]);
 
         Ok(())
     }
@@ -926,34 +1000,43 @@ mod tests {
     #[test]
     fn test_serialize_bytes() -> TestResult {
         let schema = Schema::Bytes;
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        'a'.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        "test".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        Bytes::new(&[12, 3, 7, 91, 4]).serialize(SchemaAwareSerializer::new(
-            &mut buffer,
+        assert_serialize(
+            Bytes::new(&[12, 3, 7, 91, 4]),
             &schema,
-            config,
-        )?)?;
-        assert!(
-            ().serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+            &names,
+            &[10, 12, 3, 7, 91, 4],
         );
-        assert!(
-            PhantomData::<String>
-                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize_err(
+            'a',
+            &schema,
+            &names,
+            "Failed to serialize value of type `char` using Schema::Bytes: 
Expected Schema::String",
         );
-
-        assert_eq!(
-            buffer.as_slice(),
-            &[2, b'a', 8, b't', b'e', b's', b't', 10, 12, 3, 7, 91, 4]
+        assert_serialize_err(
+            "test",
+            &schema,
+            &names,
+            "Failed to serialize value of type `str` using Schema::Bytes: 
Expected Schema::String | Schema::Uuid(String)",
+        );
+        assert_serialize_err(
+            (),
+            &schema,
+            &names,
+            "Failed to serialize value of type `unit` using Schema::Bytes: 
Expected Schema::Null",
+        );
+        assert_serialize_err(
+            PhantomData::<String>,
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `unit struct` using 
Schema::Bytes: Expected Schema::Record(name: "PhantomData", fields: [])"#,
+        );
+        assert_serialize_err(
+            Some(""),
+            &schema,
+            &names,
+            "Failed to serialize value of type `some` using Schema::Bytes: 
Expected Schema::Union([Schema::Null, _])",
         );
 
         Ok(())
@@ -962,34 +1045,39 @@ mod tests {
     #[test]
     fn test_serialize_string() -> TestResult {
         let schema = Schema::String;
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        'a'.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        "test".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        Bytes::new(&[12, 3, 7, 91, 4]).serialize(SchemaAwareSerializer::new(
-            &mut buffer,
+        assert_serialize('a', &schema, &names, &[2, b'a']);
+        assert_serialize("test", &schema, &names, &[8, b't', b'e', b's', 
b't']);
+        assert_serialize(
+            BigDecimal::new(BigInt::new(Sign::Plus, vec![50024]), 2),
             &schema,
-            config,
-        )?)?;
-        assert!(
-            ().serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+            &names,
+            &[12, b'5', b'0', b'0', b'.', b'2', b'4'],
         );
-        assert!(
-            PhantomData::<String>
-                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize_err(
+            Bytes::new(&[12, 3, 7, 91, 4]),
+            &schema,
+            &names,
+            "Failed to serialize value of type `bytes` using Schema::String: 
Expected Schema::Bytes | Schema::Fixed | Schema::BigDecimal | Schema::Decimal | 
Schema::Uuid(Bytes | Fixed) | Schema::Duration",
         );
-
-        assert_eq!(
-            buffer.as_slice(),
-            &[2, b'a', 8, b't', b'e', b's', b't', 10, 12, 3, 7, 91, 4]
+        assert_serialize_err(
+            (),
+            &schema,
+            &names,
+            "Failed to serialize value of type `unit` using Schema::String: 
Expected Schema::Null",
+        );
+        assert_serialize_err(
+            PhantomData::<String>,
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `unit struct` using 
Schema::String: Expected Schema::Record(name: "PhantomData", fields: [])"#,
+        );
+        assert_serialize_err(
+            Some(""),
+            &schema,
+            &names,
+            "Failed to serialize value of type `some` using Schema::String: 
Expected Schema::Union([Schema::Null, _])",
         );
 
         Ok(())
@@ -1009,55 +1097,55 @@ mod tests {
         )?;
 
         #[derive(Serialize)]
-        #[serde(rename_all = "camelCase")]
+        #[serde(rename_all = "camelCase", rename = "TestRecord")]
         struct GoodTestRecord {
             string_field: String,
             int_field: i32,
         }
 
         #[derive(Serialize)]
-        #[serde(rename_all = "camelCase")]
+        #[serde(rename_all = "camelCase", rename = "TestRecord")]
         struct BadTestRecord {
             foo_string_field: String,
             bar_int_field: i32,
         }
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
         let good_record = GoodTestRecord {
             string_field: String::from("test"),
             int_field: 10,
         };
-        good_record.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
+        assert_serialize(
+            good_record,
+            &schema,
+            &names,
+            &[8, b't', b'e', b's', b't', 20],
+        );
 
         let bad_record = BadTestRecord {
             foo_string_field: String::from("test"),
             bar_int_field: 10,
         };
-        assert!(
-            bad_record
-                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize_err(
+            bad_record,
+            &schema,
+            &names,
+            r#"Missing field in record: "fooStringField""#,
         );
-
-        assert!(
-            "".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize_err(
+            "",
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `str` using 
Schema::Record(RecordSchema { name: Name { name: "TestRecord", .. }, fields: 
[RecordField { name: "stringField", schema: String, .. }, RecordField { name: 
"intField", schema: Int, .. }], .. }): Expected Schema::String | 
Schema::Uuid(String)"#,
         );
-        assert!(
-            Some("")
-                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .is_err()
+        assert_serialize_err(
+            Some(""),
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `some` using 
Schema::Record(RecordSchema { name: Name { name: "TestRecord", .. }, fields: 
[RecordField { name: "stringField", schema: String, .. }, RecordField { name: 
"intField", schema: Int, .. }], .. }): Expected Schema::Union([Schema::Null, 
_])"#,
         );
 
-        assert_eq!(buffer.as_slice(), &[8, b't', b'e', b's', b't', 20]);
-
         Ok(())
     }
 
@@ -1071,47 +1159,45 @@ mod tests {
         }"#,
         )?;
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
         #[derive(Serialize)]
         struct EmptyRecord;
-        EmptyRecord.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
+        assert_serialize(EmptyRecord, &schema, &names, &[]);
 
         #[derive(Serialize)]
+        #[serde(rename = "EmptyRecord")]
         struct NonEmptyRecord {
             foo: String,
         }
         let record = NonEmptyRecord {
             foo: "bar".to_string(),
         };
-        match record
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::FieldName(field_name)) if field_name == "foo" => (),
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        match ().serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?).map_err(Error::into_details) {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "none"); // serialize_unit() delegates 
to serialize_none()
-                assert_eq!(value, "None. Cause: Expected: Record. Got: Null");
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        assert_eq!(buffer.len(), 0);
+        assert_serialize_err(record, &schema, &names, r#"Missing field in 
record: "foo""#);
+        assert_serialize_err(
+            (),
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `unit` using 
Schema::Record(RecordSchema { name: Name { name: "EmptyRecord", .. }, fields: 
[], .. }): Expected Schema::Null"#,
+        );
+        assert_serialize_err(
+            "",
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `str` using 
Schema::Record(RecordSchema { name: Name { name: "EmptyRecord", .. }, fields: 
[], .. }): Expected Schema::String | Schema::Uuid(String)"#,
+        );
+        assert_serialize_err(
+            PhantomData::<String>,
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `unit struct` using 
Schema::Record(RecordSchema { name: Name { name: "EmptyRecord", .. }, fields: 
[], .. }): Expected Schema::Record(name: "PhantomData", fields: [])"#,
+        );
+        assert_serialize_err(
+            Some(""),
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `some` using 
Schema::Record(RecordSchema { name: Name { name: "EmptyRecord", .. }, fields: 
[], .. }): Expected Schema::Union([Schema::Null, _])"#,
+        );
 
         Ok(())
     }
@@ -1127,6 +1213,7 @@ mod tests {
         )?;
 
         #[derive(Serialize)]
+        #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
         enum Suit {
             Spades,
             Hearts,
@@ -1134,35 +1221,18 @@ mod tests {
             Clubs,
         }
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        Suit::Spades.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
-        Suit::Hearts.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
-        Suit::Diamonds.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
-        Suit::Clubs.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        match None::<()>
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "none");
-                assert_eq!(value, "None. Cause: Expected: Enum. Got: Null");
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        assert_eq!(buffer.as_slice(), &[0, 2, 4, 6]);
+        assert_serialize(Suit::Spades, &schema, &names, &[0]);
+        assert_serialize(Suit::Hearts, &schema, &names, &[2]);
+        assert_serialize(Suit::Diamonds, &schema, &names, &[4]);
+        assert_serialize(Suit::Clubs, &schema, &names, &[6]);
+        assert_serialize_err(
+            None::<()>,
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `none` using 
Schema::Enum(EnumSchema { name: Name { name: "Suit", .. }, symbols: ["SPADES", 
"HEARTS", "DIAMONDS", "CLUBS"], .. }): Expected Schema::Union([Schema::Null, 
_])"#,
+        );
 
         Ok(())
     }
@@ -1176,34 +1246,20 @@ mod tests {
         }"#,
         )?;
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
-
-        let arr: Vec<i64> = vec![10, 5, 400];
-        arr.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-
-        match vec![1_f32]
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "f32");
-                assert_eq!(value, "1. Cause: Expected: Long. Got: Float");
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
 
-        assert_eq!(buffer.as_slice(), &[6, 20, 10, 160, 6, 0]);
+        assert_serialize(
+            vec![10i64, 5, 400],
+            &schema,
+            &names,
+            &[6, 20, 10, 160, 6, 0],
+        );
+        assert_serialize_err(
+            vec![1_f32],
+            &schema,
+            &names,
+            "Failed to serialize value of type `f32` using Schema::Long: 
Expected Schema::Float",
+        );
 
         Ok(())
     }
@@ -1217,44 +1273,29 @@ mod tests {
         }"#,
         )?;
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
         let mut map: BTreeMap<String, i64> = BTreeMap::new();
         map.insert(String::from("item1"), 10);
         map.insert(String::from("item2"), 400);
 
-        map.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
+        assert_serialize(
+            map,
+            &schema,
+            &names,
+            &[
+                4, 10, b'i', b't', b'e', b'm', b'1', 20, 10, b'i', b't', b'e', 
b'm', b'2', 160, 6,
+                0,
+            ],
+        );
 
         let mut map: BTreeMap<String, &str> = BTreeMap::new();
         map.insert(String::from("item1"), "value1");
-        match map
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "string");
-                assert_eq!(value, "value1. Cause: Expected: Long. Got: 
String");
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        assert_eq!(
-            buffer.as_slice(),
-            &[
-                4, 10, b'i', b't', b'e', b'm', b'1', 20, 10, b'i', b't', b'e', 
b'm', b'2', 160, 6,
-                0
-            ]
+        assert_serialize_err(
+            map,
+            &schema,
+            &names,
+            "Failed to serialize value of type `str` using Schema::Long: 
Expected Schema::String | Schema::Uuid(String)",
         );
 
         Ok(())
@@ -1274,43 +1315,20 @@ mod tests {
             Long(i64),
         }
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        Some(10i64).serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        None::<i64>.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        NullableLong::Long(400).serialize(SchemaAwareSerializer::new(
-            &mut buffer,
+        assert_serialize(Some(10i64), &schema, &names, &[2, 20]);
+        assert_serialize(None::<i64>, &schema, &names, &[0]);
+        assert_serialize(NullableLong::Long(400), &schema, &names, &[2, 160, 
6]);
+        assert_serialize(NullableLong::Null, &schema, &names, &[0]);
+        assert_serialize(400i64, &schema, &names, &[2, 160, 6]);
+        assert_serialize((), &schema, &names, &[0]);
+        assert_serialize_err(
+            "invalid",
             &schema,
-            config,
-        )?)?;
-        NullableLong::Null.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
-
-        match "invalid"
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "string");
-                assert_eq!(
-                    value,
-                    "invalid. Cause: Expected one of the union variants [Null, 
Long]. Got: String"
-                );
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        assert_eq!(buffer.as_slice(), &[2, 20, 0, 2, 160, 6, 0]);
+            &names,
+            "Failed to serialize value of type `str` using 
Schema::Union(UnionSchema { schemas: [Null, Long] }): Expected Schema::String 
in variants",
+        );
 
         Ok(())
     }
@@ -1330,48 +1348,23 @@ mod tests {
             Str(String),
         }
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
-
-        LongOrString::Null.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
-        LongOrString::Long(400).serialize(SchemaAwareSerializer::new(
-            &mut buffer,
+        assert_serialize(LongOrString::Null, &schema, &names, &[0]);
+        assert_serialize(LongOrString::Long(400), &schema, &names, &[2, 160, 
6]);
+        assert_serialize(
+            LongOrString::Str("test".into()),
             &schema,
-            config,
-        )?)?;
-        
LongOrString::Str(String::from("test")).serialize(SchemaAwareSerializer::new(
-            &mut buffer,
+            &names,
+            &[4, 8, b't', b'e', b's', b't'],
+        );
+        assert_serialize((), &schema, &names, &[0]);
+        assert_serialize(400i64, &schema, &names, &[2, 160, 6]);
+        assert_serialize("test", &schema, &names, &[4, 8, b't', b'e', b's', 
b't']);
+        assert_serialize_err(
+            1f64,
             &schema,
-            config,
-        )?)?;
-
-        match 1_f64
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "f64");
-                assert_eq!(
-                    value,
-                    "1. Cause: Cannot find a Double schema in [Null, Long, 
String]"
-                );
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        assert_eq!(
-            buffer.as_slice(),
-            &[0, 2, 160, 6, 4, 8, b't', b'e', b's', b't']
+            &names,
+            "Failed to serialize value of type `f64` using 
Schema::Union(UnionSchema { schemas: [Null, Long, String] }): Expected 
Schema::Double in variants",
         );
 
         Ok(())
@@ -1387,75 +1380,32 @@ mod tests {
         }"#,
         )?;
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        Bytes::new(&[10, 124, 31, 97, 14, 201, 3, 
88]).serialize(SchemaAwareSerializer::new(
-            &mut buffer,
+        assert_serialize(
+            Bytes::new(&[10, 124, 31, 97, 14, 201, 3, 88]),
             &schema,
-            config,
-        )?)?;
-
-        // non-8 size
-        match Bytes::new(&[123])
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "bytes");
-                assert_eq!(
-                    value,
-                    "7b. Cause: Fixed schema size (8) does not match the value 
length (1)"
-                ); // Bytes represents its values as hexadecimals: '7b' is 123
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        // array
-        match [1; 8]
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "tuple"); // TODO: why is this 'tuple' 
?!
-                assert_eq!(value, "tuple (len=8). Cause: Expected: Fixed. Got: 
Array");
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        // slice
-        match &[1, 2, 3, 4, 5, 6, 7, 8]
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(*value_type, "tuple"); // TODO: why is this 'tuple' 
?!
-                assert_eq!(value, "tuple (len=8). Cause: Expected: Fixed. Got: 
Array");
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        assert_eq!(buffer.as_slice(), &[10, 124, 31, 97, 14, 201, 3, 88]);
+            &names,
+            &[10, 124, 31, 97, 14, 201, 3, 88],
+        );
+        assert_serialize_err(
+            Bytes::new(&[123]),
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `bytes` using 
Schema::Fixed(FixedSchema { name: Name { name: "LongVal", .. }, size: 8, .. }): 
Fixed size (8) does not match bytes length (1)"#,
+        );
+        assert_serialize_err(
+            [1u8; 8],
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `tuple` using 
Schema::Fixed(FixedSchema { name: Name { name: "LongVal", .. }, size: 8, .. }): 
Expected Schema::Record(fields.len() == 8)"#,
+        );
+        assert_serialize_err(
+            [1u8, 2, 3, 4, 5, 6, 7, 8].as_slice(),
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `seq` using 
Schema::Fixed(FixedSchema { name: Name { name: "LongVal", .. }, size: 8, .. }): 
Expected Schema::Array"#,
+        );
 
         Ok(())
     }
@@ -1471,31 +1421,16 @@ mod tests {
         }"#,
         )?;
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
         let val = Decimal::from(&[251, 155]);
-        val.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-
-        match ().serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?).map_err(Error::into_details) {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "none");
-                assert_eq!(value, "None. Cause: Expected: Decimal. Got: Null");
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        assert_eq!(buffer.as_slice(), &[4, 251, 155]);
+        assert_serialize(val, &schema, &names, &[4, 251, 155]);
+        assert_serialize_err(
+            (),
+            &schema,
+            &names,
+            "Failed to serialize value of type `unit` using 
Schema::Decimal(DecimalSchema { precision: 16, scale: 2, inner: Bytes }): 
Expected Schema::Null",
+        );
 
         Ok(())
     }
@@ -1513,31 +1448,16 @@ mod tests {
         }"#,
         )?;
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
         let val = Decimal::from(&[0, 0, 0, 0, 0, 0, 251, 155]);
-        val.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-
-        match ().serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?).map_err(Error::into_details) {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "none");
-                assert_eq!(value, "None. Cause: Expected: Decimal. Got: Null");
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        assert_eq!(buffer.as_slice(), &[0, 0, 0, 0, 0, 0, 251, 155]);
+        assert_serialize(val, &schema, &names, &[0, 0, 0, 0, 0, 0, 251, 155]);
+        assert_serialize_err(
+            (),
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `unit` using 
Schema::Decimal(DecimalSchema { precision: 16, scale: 2, inner: 
Fixed(FixedSchema { name: Name { name: "FixedDecimal", .. }, size: 8, 
attributes: {"precision": Number(16), "scale": Number(2)}, .. }) }): Expected 
Schema::Null"#,
+        );
 
         Ok(())
     }
@@ -1551,18 +1471,21 @@ mod tests {
         }"#,
         )?;
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        let val = BigDecimal::new(BigInt::new(Sign::Plus, vec![50024]), 2);
-        val.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
+        #[derive(Serialize)]
+        #[serde(transparent)]
+        struct BigDecimalWrapper {
+            // This is needed because the Serialize implementation of 
BigDecimal serializes to a string.
+            // The with implementation serializes to bytes.
+            #[serde(with = "crate::serde::bigdecimal")]
+            value: BigDecimal,
+        }
 
-        assert_eq!(buffer.as_slice(), &[10, 6, 0, 195, 104, 4]);
+        let val = BigDecimalWrapper {
+            value: BigDecimal::new(BigInt::new(Sign::Plus, vec![50024]), 2),
+        };
+        assert_serialize(val, &schema, &names, &[10, 6, 0, 195, 104, 4]);
 
         Ok(())
     }
@@ -1578,40 +1501,25 @@ mod tests {
         }"#,
         )?;
 
+        // Uuid serialize implementation changes based on this value
         assert!(!crate::util::is_human_readable());
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
-
-        "8c28da81-238c-4326-bddd-4e3d00cc5099"
-            .parse::<Uuid>()?
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
 
-        match 1_u8
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "u8");
-                assert_eq!(value, "1. Cause: Expected: Uuid. Got: Int");
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
+        let uuid = "8c28da81-238c-4326-bddd-4e3d00cc5099".parse::<Uuid>()?;
 
-        assert_eq!(
-            buffer.as_slice(),
+        assert_serialize(
+            uuid,
+            &schema,
+            &names,
             &[
-                140, 40, 218, 129, 35, 140, 67, 38, 189, 221, 78, 61, 0, 204, 
80, 153
-            ]
+                140, 40, 218, 129, 35, 140, 67, 38, 189, 221, 78, 61, 0, 204, 
80, 153,
+            ],
+        );
+        assert_serialize_err(
+            1u8,
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `u8` using 
Schema::Uuid(Fixed(FixedSchema { name: Name { name: "FixedUuid", .. }, size: 
16, .. })): Expected Schema::Int | Schema::Date | Schema::TimeMillis"#,
         );
 
         Ok(())
@@ -1626,39 +1534,23 @@ mod tests {
         }"#,
         )?;
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
-
-        100_u8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        1000_u16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        10000_u32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        1000_i16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        10000_i32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-
-        match 10000_f32
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "f32");
-                assert_eq!(value, "10000. Cause: Expected: Date. Got: Float");
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
 
-        assert_eq!(
-            buffer.as_slice(),
-            &[200, 1, 208, 15, 160, 156, 1, 208, 15, 160, 156, 1]
+        assert_serialize(100u8, &schema, &names, &[200, 1]);
+        assert_serialize(1000u16, &schema, &names, &[208, 15]);
+        assert_serialize(1000i16, &schema, &names, &[208, 15]);
+        assert_serialize(10000i32, &schema, &names, &[160, 156, 1]);
+        assert_serialize_err(
+            10000u32,
+            &schema,
+            &names,
+            "Failed to serialize value of type `u32` using Schema::Date: 
Expected Schema::Long | Schema::TimeMicros | 
Schema::{,Local}Timestamp{Millis,Micros,Nanos}",
+        );
+        assert_serialize_err(
+            10000f32,
+            &schema,
+            &names,
+            "Failed to serialize value of type `f32` using Schema::Date: 
Expected Schema::Float",
         );
 
         Ok(())
@@ -1673,39 +1565,23 @@ mod tests {
         }"#,
         )?;
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
-
-        100_u8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        1000_u16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        10000_u32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        1000_i16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        10000_i32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
 
-        match 10000_f32
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "f32");
-                assert_eq!(value, "10000. Cause: Expected: TimeMillis. Got: 
Float");
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        assert_eq!(
-            buffer.as_slice(),
-            &[200, 1, 208, 15, 160, 156, 1, 208, 15, 160, 156, 1]
+        assert_serialize(100u8, &schema, &names, &[200, 1]);
+        assert_serialize(1000u16, &schema, &names, &[208, 15]);
+        assert_serialize(1000i16, &schema, &names, &[208, 15]);
+        assert_serialize(10000i32, &schema, &names, &[160, 156, 1]);
+        assert_serialize_err(
+            10000u32,
+            &schema,
+            &names,
+            "Failed to serialize value of type `u32` using Schema::TimeMillis: 
Expected Schema::Long | Schema::TimeMicros | 
Schema::{,Local}Timestamp{Millis,Micros,Nanos}",
+        );
+        assert_serialize_err(
+            10000f32,
+            &schema,
+            &names,
+            "Failed to serialize value of type `f32` using Schema::TimeMillis: 
Expected Schema::Float",
         );
 
         Ok(())
@@ -1720,42 +1596,39 @@ mod tests {
         }"#,
         )?;
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        100_u8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        1000_u16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        10000_u32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        1000_i16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        10000_i32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        10000_i64.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-
-        match 10000_f32
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "f32");
-                assert_eq!(value, "10000. Cause: Expected: TimeMicros. Got: 
Float");
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        assert_eq!(
-            buffer.as_slice(),
-            &[
-                200, 1, 208, 15, 160, 156, 1, 208, 15, 160, 156, 1, 160, 156, 1
-            ]
+        assert_serialize(10000u32, &schema, &names, &[160, 156, 1]);
+        assert_serialize(10000i64, &schema, &names, &[160, 156, 1]);
+        assert_serialize_err(
+            100u8,
+            &schema,
+            &names,
+            "Failed to serialize value of type `u8` using Schema::TimeMicros: 
Expected Schema::Int | Schema::Date | Schema::TimeMillis",
+        );
+        assert_serialize_err(
+            1000u16,
+            &schema,
+            &names,
+            "Failed to serialize value of type `u16` using Schema::TimeMicros: 
Expected Schema::Int | Schema::Date | Schema::TimeMillis",
+        );
+        assert_serialize_err(
+            1000i16,
+            &schema,
+            &names,
+            "Failed to serialize value of type `i16` using Schema::TimeMicros: 
Expected Schema::Int | Schema::Date | Schema::TimeMillis",
+        );
+        assert_serialize_err(
+            10000i32,
+            &schema,
+            &names,
+            "Failed to serialize value of type `i32` using Schema::TimeMicros: 
Expected Schema::Int | Schema::Date | Schema::TimeMillis",
+        );
+        assert_serialize_err(
+            10000f32,
+            &schema,
+            &names,
+            "Failed to serialize value of type `f32` using Schema::TimeMicros: 
Expected Schema::Float",
         );
 
         Ok(())
@@ -1763,7 +1636,11 @@ mod tests {
 
     #[test]
     fn test_serialize_timestamp() -> TestResult {
-        for precision in ["millis", "micros", "nanos"] {
+        for (precision, error) in [
+            ("millis", "Millis"),
+            ("micros", "Micros"),
+            ("nanos", "Nanos"),
+        ] {
             let schema = Schema::parse_str(&format!(
                 r#"{{
                 "type": "long",
@@ -1771,50 +1648,49 @@ mod tests {
             }}"#
             ))?;
 
-            let mut buffer: Vec<u8> = Vec::new();
             let names = HashMap::new();
-            let config = Config::<'_, Schema> {
-                names: &names,
-                target_block_size: None,
-                human_readable: false,
-            };
-            100_u8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-            1000_u16.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
-            10000_u32.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
-            1000_i16.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
-            10000_i32.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
-            10000_i64.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
-
-            match 10000_f64
-                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-                .map_err(Error::into_details)
-            {
-                Err(Details::SerializeValueWithSchema {
-                    value_type,
-                    value,
-                    schema,
-                }) => {
-                    let mut capital_precision = precision.to_string();
-                    if let Some(c) = capital_precision.chars().next() {
-                        capital_precision.replace_range(..1, 
&c.to_uppercase().to_string());
-                    }
-                    assert_eq!(value_type, "f64");
-                    assert_eq!(
-                        value,
-                        format!(
-                            "10000. Cause: Expected: 
Timestamp{capital_precision}. Got: Double"
-                        )
-                    );
-                    assert_eq!(schema, schema);
-                }
-                unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-            }
 
-            assert_eq!(
-                buffer.as_slice(),
-                &[
-                    200, 1, 208, 15, 160, 156, 1, 208, 15, 160, 156, 1, 160, 
156, 1
-                ]
+            assert_serialize(10000u32, &schema, &names, &[160, 156, 1]);
+            assert_serialize(10000i64, &schema, &names, &[160, 156, 1]);
+            assert_serialize_err(
+                100u8,
+                &schema,
+                &names,
+                &format!(
+                    "Failed to serialize value of type `u8` using 
Schema::Timestamp{error}: Expected Schema::Int | Schema::Date | 
Schema::TimeMillis"
+                ),
+            );
+            assert_serialize_err(
+                1000u16,
+                &schema,
+                &names,
+                &format!(
+                    "Failed to serialize value of type `u16` using 
Schema::Timestamp{error}: Expected Schema::Int | Schema::Date | 
Schema::TimeMillis"
+                ),
+            );
+            assert_serialize_err(
+                1000i16,
+                &schema,
+                &names,
+                &format!(
+                    "Failed to serialize value of type `i16` using 
Schema::Timestamp{error}: Expected Schema::Int | Schema::Date | 
Schema::TimeMillis"
+                ),
+            );
+            assert_serialize_err(
+                10000i32,
+                &schema,
+                &names,
+                &format!(
+                    "Failed to serialize value of type `i32` using 
Schema::Timestamp{error}: Expected Schema::Int | Schema::Date | 
Schema::TimeMillis"
+                ),
+            );
+            assert_serialize_err(
+                10000f32,
+                &schema,
+                &names,
+                &format!(
+                    "Failed to serialize value of type `f32` using 
Schema::Timestamp{error}: Expected Schema::Float"
+                ),
             );
         }
 
@@ -1832,38 +1708,21 @@ mod tests {
         }"#,
         )?;
 
-        let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
-        let config = Config::<'_, Schema> {
-            names: &names,
-            target_block_size: None,
-            human_readable: false,
-        };
 
-        let duration_bytes =
-            ByteArray::new(Duration::new(Months::new(3), Days::new(2), 
Millis::new(1200)).into());
-        duration_bytes.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
-
-        match [1; 12]
-            .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
-            .map_err(Error::into_details)
-        {
-            Err(Details::SerializeValueWithSchema {
-                value_type,
-                value,
-                schema,
-            }) => {
-                assert_eq!(value_type, "tuple"); // TODO: why is this 'tuple' 
?!
-                assert_eq!(
-                    value,
-                    "tuple (len=12). Cause: Expected: Duration. Got: Array"
-                );
-                assert_eq!(schema, schema);
-            }
-            unexpected => panic!("Expected an error. Got: {unexpected:?}"),
-        }
-
-        assert_eq!(buffer.as_slice(), &[3, 0, 0, 0, 2, 0, 0, 0, 176, 4, 0, 0]);
+        let duration = Duration::new(Months::new(3), Days::new(2), 
Millis::new(1200));
+        assert_serialize(
+            duration,
+            &schema,
+            &names,
+            &[3, 0, 0, 0, 2, 0, 0, 0, 176, 4, 0, 0],
+        );
+        assert_serialize_err(
+            [0u8; 12],
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `tuple` using 
Schema::Duration(FixedSchema { name: Name { name: "duration", .. }, size: 12, 
.. }): Expected Schema::Record(fields.len() == 12)"#,
+        );
 
         Ok(())
     }
@@ -1917,6 +1776,17 @@ mod tests {
                 inner_record: None,
             })),
         };
+        assert_serialize(
+            good_record,
+            &schema,
+            rs.get_names(),
+            &[
+                8, 116, 101, 115, 116, 20, 10, 6, 0, 195, 104, 4, 140, 40, 
218, 129, 35, 140, 67,
+                38, 189, 221, 78, 61, 0, 204, 80, 152, 2, 20, 105, 110, 110, 
101, 114, 95, 116,
+                101, 115, 116, 200, 1, 8, 4, 78, 70, 4, 140, 40, 218, 129, 35, 
140, 67, 38, 189,
+                221, 78, 61, 0, 204, 80, 153, 0,
+            ],
+        );
         good_record.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
 
         assert_eq!(
diff --git a/avro/src/serde/with.rs b/avro/src/serde/with.rs
index 29744e1..74c0a3a 100644
--- a/avro/src/serde/with.rs
+++ b/avro/src/serde/with.rs
@@ -503,6 +503,315 @@ pub mod slice_opt {
     }
 }
 
+/// (De)serialize [`BigDecimal`] as a [`Schema::BigDecimal`] instead of a 
[`Schema::String`].
+///
+/// This module is intended to be used through the Serde `with` attribute.
+///
+/// Use [`apache_avro::serde::bigdecimal_opt`] for optional big decimals 
values.
+///
+/// When used with different serialization formats, this will write bytes.
+///
+/// See usage with below example:
+/// ```
+/// # use apache_avro::AvroSchema;
+/// # use serde::{Deserialize, Serialize};
+/// #[derive(AvroSchema, Serialize, Deserialize)]
+/// struct StructWithBigDecimal<'a> {
+///     #[avro(with)]
+///     #[serde(with = "apache_avro::serde::bigdecimal")]
+///     decimal: BigDecimal,
+/// }
+/// ```
+///
+/// [`BigDecimal`]: ::bigdecimal::BigDecimal
+/// [`Schema::BigDecimal`]: crate::Schema::BigDecimal
+/// [`Schema::String`]: crate::Schema::String
+/// [`apache_avro::serde::bigdecimal_opt`]: bigdecimal_opt
+pub mod bigdecimal {
+    use std::collections::HashSet;
+
+    use bigdecimal::BigDecimal;
+    use serde::{Deserializer, Serializer, de::Error as _, ser::Error as _};
+
+    use crate::{
+        Schema,
+        bigdecimal::{big_decimal_as_bytes, deserialize_big_decimal},
+        schema::{Name, NamespaceRef, RecordField},
+        serde::with::BytesType,
+    };
+
+    /// Returns [`Schema::BigDecimal`]
+    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: NamespaceRef) -> 
Schema {
+        Schema::BigDecimal
+    }
+
+    /// Returns `None`
+    pub fn get_record_fields_in_ctxt(
+        _: &mut HashSet<Name>,
+        _: NamespaceRef,
+    ) -> Option<Vec<RecordField>> {
+        None
+    }
+
+    pub fn serialize<S>(decimal: &BigDecimal, serializer: S) -> Result<S::Ok, 
S::Error>
+    where
+        S: Serializer,
+    {
+        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
+        let decimal_bytes = 
big_decimal_as_bytes(decimal).map_err(S::Error::custom)?;
+        serde_bytes::serialize(&decimal_bytes, serializer)
+    }
+
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<BigDecimal, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let _bytes_guard = super::BytesTypeGuard::set(BytesType::Bytes);
+        let _guard = super::BorrowedGuard::set(true);
+        // We don't use &'de [u8] here as the deserializer doesn't support that
+        let bytes: Vec<u8> = serde_bytes::deserialize(deserializer)?;
+
+        deserialize_big_decimal(&bytes).map_err(D::Error::custom)
+    }
+}
+
+/// (De)serialize [`Option<BigDecimal>`] as a `Schema::Union(Schema::Null, 
Schema::BigDecimal)` instead of a `Schema::Union(Schema::Null, Schema::String)`.
+///
+/// This module is intended to be used through the Serde `with` attribute.
+///
+/// Use [`apache_avro::serde::bigdecimal`] for non-optional big decimals 
values.
+///
+/// When used with different serialization formats, this will write bytes.
+///
+/// See usage with below example:
+/// ```
+/// # use apache_avro::AvroSchema;
+/// # use serde::{Deserialize, Serialize};
+/// #[derive(AvroSchema, Serialize, Deserialize)]
+/// struct StructWithBigDecimal<'a> {
+///     #[avro(with)]
+///     #[serde(with = "apache_avro::serde::bigdecimal_opt")]
+///     decimal: Option<BigDecimal>,
+/// }
+/// ```
+///
+/// [`Option<BigDecimal>`]: ::bigdecimal::BigDecimal
+/// [`apache_avro::serde::bigdecimal`]: bigdecimal
+pub mod bigdecimal_opt {
+    use std::collections::HashSet;
+
+    use bigdecimal::BigDecimal;
+    use serde::{Deserializer, Serializer, de::Error as _, ser::Error as _};
+
+    use crate::{
+        Schema,
+        bigdecimal::{big_decimal_as_bytes, deserialize_big_decimal},
+        schema::{Name, NamespaceRef, RecordField, UnionSchema},
+        serde::with::BytesType,
+    };
+
+    /// Returns `Schema::Union(Schema::Null, Schema::BigDecimal)`
+    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: NamespaceRef) -> 
Schema {
+        Schema::Union(
+            UnionSchema::new(vec![Schema::Null, Schema::BigDecimal])
+                .expect("This is a valid union"),
+        )
+    }
+
+    /// Returns `None`
+    pub fn get_record_fields_in_ctxt(
+        _: &mut HashSet<Name>,
+        _: NamespaceRef,
+    ) -> Option<Vec<RecordField>> {
+        None
+    }
+
+    pub fn serialize<S>(decimal: &Option<BigDecimal>, serializer: S) -> 
Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
+        if let Some(decimal) = decimal {
+            let decimal_bytes = 
big_decimal_as_bytes(decimal).map_err(S::Error::custom)?;
+            serde_bytes::serialize(&Some(decimal_bytes), serializer)
+        } else {
+            serde_bytes::serialize(&None::<Vec<u8>>, serializer)
+        }
+    }
+
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<BigDecimal>, 
D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let _bytes_guard = super::BytesTypeGuard::set(BytesType::Bytes);
+        let _guard = super::BorrowedGuard::set(true);
+        let bytes: Option<Vec<u8>> = serde_bytes::deserialize(deserializer)?;
+        if let Some(bytes) = bytes {
+            deserialize_big_decimal(&bytes)
+                .map(Some)
+                .map_err(D::Error::custom)
+        } else {
+            Ok(None)
+        }
+    }
+}
+
+/// (De)serialize an Rust array (`[T; N]`) as an Avro [`Schema::Array`].
+///
+/// This module is intended to be used through the Serde `with` attribute.
+///
+/// Use [`apache_avro::serde::array_opt`] for optional array values.
+///
+/// See usage with below example:
+/// ```
+/// # use apache_avro::AvroSchema;
+/// # use serde::{Deserialize, Serialize};
+/// #[derive(AvroSchema, Serialize, Deserialize)]
+/// struct StructWithBytes<'a> {
+///     #[avro(with = apache_avro::serde::array::get_schema_in_ctxt::<i32>)]
+///     #[serde(with = "apache_avro::serde::array")]
+///     array: [i32; 10],
+/// }
+/// ```
+///
+/// [`apache_avro::serde::array_opt`]: array_opt
+/// [`Schema::Array`]: crate::schema::Schema::Array
+pub mod array {
+    use crate::{
+        AvroSchemaComponent, Schema,
+        schema::{Name, NamespaceRef, RecordField},
+    };
+    use serde::de::DeserializeOwned;
+    use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as 
_};
+    use std::collections::HashSet;
+
+    /// Returns `Schema::Array(T::get_schema_in_ctxt())`
+    pub fn get_schema_in_ctxt<T: AvroSchemaComponent>(
+        named_schemas: &mut HashSet<Name>,
+        enclosing_namespace: NamespaceRef,
+    ) -> Schema {
+        Schema::array(T::get_schema_in_ctxt(named_schemas, 
enclosing_namespace)).build()
+    }
+
+    /// Returns `None`
+    pub fn get_record_fields_in_ctxt(
+        _: &mut HashSet<Name>,
+        _: NamespaceRef,
+    ) -> Option<Vec<RecordField>> {
+        None
+    }
+
+    pub fn serialize<const N: usize, S, T>(value: &[T; N], serializer: S) -> 
Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+        T: Serialize,
+    {
+        value.as_slice().serialize(serializer)
+    }
+
+    pub fn deserialize<'de, const N: usize, D, T>(deserializer: D) -> 
Result<[T; N], D::Error>
+    where
+        D: Deserializer<'de>,
+        T: DeserializeOwned,
+    {
+        let bytes = <Vec<T> as Deserialize>::deserialize(deserializer)?;
+        bytes.try_into().map_err(|v: Vec<T>| {
+            D::Error::custom(format!(
+                "Deserialized array has length {} which does not match array 
length of {N}",
+                v.len()
+            ))
+        })
+    }
+}
+
+/// (De)serialize an optional Rust array (`Option<[T; N]>`) as an Avro 
`Schema::Union([Schema::Null, Schema::Array])`.
+///
+/// This module is intended to be used through the Serde `with` attribute.
+///
+/// Use [`apache_avro::serde::array`] for non-optional array values.
+///
+/// When used with different serialization formats, this is equivalent to 
[`serde_bytes`].
+///
+/// See usage with below example:
+/// ```
+/// # use apache_avro::AvroSchema;
+/// # use serde::{Deserialize, Serialize};
+/// #[derive(AvroSchema, Serialize, Deserialize)]
+/// struct StructWithBytes<'a> {
+///     #[avro(with = 
apache_avro::serde::array_opt::get_schema_in_ctxt::<i32>)]
+///     #[serde(with = "apache_avro::serde::array_opt")]
+///     array: Option<[i32; 10]>,
+/// }
+/// ```
+///
+/// [`apache_avro::serde::array`]: mod@array
+pub mod array_opt {
+    use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as 
_};
+    use std::collections::HashSet;
+
+    use crate::{
+        AvroSchemaComponent, Schema,
+        schema::{Name, NamespaceRef, RecordField, UnionSchema},
+    };
+
+    /// Returns `Schema::Union(Schema::Null, 
Schema::Array(T::get_schema_in_ctxt()))`
+    pub fn get_schema_in_ctxt<T: AvroSchemaComponent>(
+        named_schemas: &mut HashSet<Name>,
+        enclosing_namespace: NamespaceRef,
+    ) -> Schema {
+        Schema::Union(
+            UnionSchema::new(vec![
+                Schema::Null,
+                Schema::array(T::get_schema_in_ctxt(named_schemas, 
enclosing_namespace)).build(),
+            ])
+            .expect("This is a valid union"),
+        )
+    }
+
+    /// Returns `None`
+    pub fn get_record_fields_in_ctxt(
+        _: &mut HashSet<Name>,
+        _: NamespaceRef,
+    ) -> Option<Vec<RecordField>> {
+        None
+    }
+
+    pub fn serialize<const N: usize, S, T>(
+        value: &Option<[T; N]>,
+        serializer: S,
+    ) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+        T: Serialize,
+    {
+        if let Some(array) = value {
+            Some(array.as_slice()).serialize(serializer)
+        } else {
+            None::<Vec<T>>.serialize(serializer)
+        }
+    }
+
+    pub fn deserialize<'de, const N: usize, D, T>(
+        deserializer: D,
+    ) -> Result<Option<[T; N]>, D::Error>
+    where
+        D: Deserializer<'de>,
+        T: Deserialize<'de>,
+    {
+        let bytes = <Option<Vec<T>> as 
Deserialize>::deserialize(deserializer)?;
+        if let Some(bytes) = bytes {
+            Ok(Some(bytes.try_into().map_err(|v: Vec<T>| {
+                D::Error::custom(format!(
+                    "Deserialized array has length {} which does not match 
array length of {N}",
+                    v.len()
+                ))
+            })?))
+        } else {
+            Ok(None)
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use crate::{Schema, from_value, to_value, types::Value};


Reply via email to