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

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

commit 983d90a87c2cbda6311dd1c53049a551f3f8e94a
Author: default <[email protected]>
AuthorDate: Tue Feb 24 13:23:07 2026 +0000

    fix tests
---
 avro/src/bigdecimal.rs               |   3 +-
 avro/src/schema/mod.rs               |   8 +-
 avro/src/schema/union.rs             |   4 +-
 avro/src/serde/mod.rs                |   8 +-
 avro/src/serde/ser.rs                |   2 +-
 avro/src/serde/ser_schema/mod.rs     |  61 -------
 avro/src/serde/ser_schema2/mod.rs    | 303 +++++++++++++++++++-------------
 avro/src/serde/ser_schema2/record.rs |   3 +-
 avro/src/serde/ser_schema2/union.rs  | 133 +++++++++------
 avro/src/serde/with.rs               | 322 ++++++++++++++++++++++++++++++++++-
 10 files changed, 603 insertions(+), 244 deletions(-)

diff --git a/avro/src/bigdecimal.rs b/avro/src/bigdecimal.rs
index b887535..7a6a379 100644
--- a/avro/src/bigdecimal.rs
+++ b/avro/src/bigdecimal.rs
@@ -47,8 +47,7 @@ pub(crate) fn serialize_big_decimal(decimal: &BigDecimal) -> 
AvroResult<Vec<u8>>
     Ok(final_buffer)
 }
 
-pub(crate) fn deserialize_big_decimal(bytes: &Vec<u8>) -> 
AvroResult<BigDecimal> {
-    let mut bytes: &[u8] = bytes.as_slice();
+pub(crate) fn deserialize_big_decimal(mut bytes: &[u8]) -> 
AvroResult<BigDecimal> {
     let mut big_decimal_buffer = match decode_len(&mut bytes) {
         Ok(size) => vec![0u8; size],
         Err(err) => return Err(Details::BigDecimalLen(Box::new(err)).into()),
diff --git a/avro/src/schema/mod.rs b/avro/src/schema/mod.rs
index f098882..f433335 100644
--- a/avro/src/schema/mod.rs
+++ b/avro/src/schema/mod.rs
@@ -28,14 +28,14 @@ pub(crate) use crate::schema::resolve::{
     ResolvedOwnedSchema, resolve_names, resolve_names_with_schemata,
 };
 pub use crate::schema::{
-    name::{Alias, Aliases, Name, Names, NamesRef, Namespace},
-    record::{
-        RecordField, RecordFieldBuilder, RecordFieldOrder, RecordSchema, 
RecordSchemaBuilder,
-    },
     builders::{
         SchemaArrayBuilder, SchemaEnumBuilder, SchemaFixedBuilder, 
SchemaMapBuilder,
         SchemaRecordBuilder,
     },
+    name::{Alias, Aliases, Name, Names, NamesRef, Namespace},
+    record::{
+        RecordField, RecordFieldBuilder, RecordFieldOrder, RecordSchema, 
RecordSchemaBuilder,
+    },
     resolve::ResolvedSchema,
     union::UnionSchema,
 };
diff --git a/avro/src/schema/union.rs b/avro/src/schema/union.rs
index 1312e4a..f06815e 100644
--- a/avro/src/schema/union.rs
+++ b/avro/src/schema/union.rs
@@ -17,8 +17,8 @@
 
 use crate::error::Details;
 use crate::schema::{
-    DecimalSchema, InnerDecimalSchema, Name, Namespace, ResolvedSchema, Schema,
-    SchemaKind, UuidSchema,
+    DecimalSchema, InnerDecimalSchema, Name, Namespace, ResolvedSchema, 
Schema, SchemaKind,
+    UuidSchema,
 };
 use crate::types;
 use crate::{AvroResult, Error};
diff --git a/avro/src/serde/mod.rs b/avro/src/serde/mod.rs
index 281bf9a..6f70101 100644
--- a/avro/src/serde/mod.rs
+++ b/avro/src/serde/mod.rs
@@ -118,7 +118,13 @@ 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,
+};
+
+// TODO: temp
+pub use ser_schema2::SchemaAwareSerializer;
 
 #[doc(hidden)]
 pub use derive::get_record_fields_in_ctxt;
diff --git a/avro/src/serde/ser.rs b/avro/src/serde/ser.rs
index 76c6579..efd6fe8 100644
--- a/avro/src/serde/ser.rs
+++ b/avro/src/serde/ser.rs
@@ -182,7 +182,7 @@ impl ser::Serializer for Serializer {
 
     fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
         match SER_BYTES_TYPE.get() {
-            BytesType::Bytes => Ok(Value::Bytes(v.to_owned())),
+            BytesType::Unset | BytesType::Bytes => 
Ok(Value::Bytes(v.to_owned())),
             BytesType::Fixed => Ok(Value::Fixed(v.len(), v.to_owned())),
         }
     }
diff --git a/avro/src/serde/ser_schema/mod.rs b/avro/src/serde/ser_schema/mod.rs
index 6c27cf1..679ea7c 100644
--- a/avro/src/serde/ser_schema/mod.rs
+++ b/avro/src/serde/ser_schema/mod.rs
@@ -3004,67 +3004,6 @@ mod tests {
         Ok(())
     }
 
-    #[test]
-    fn test_serialize_recursive_record() -> TestResult {
-        let schema = Schema::parse_str(
-            r#"{
-            "type": "record",
-            "name": "TestRecord",
-            "fields": [
-                {"name": "stringField", "type": "string"},
-                {"name": "intField", "type": "int"},
-                {"name": "bigDecimalField", "type": {"type": "bytes", 
"logicalType": "big-decimal"}},
-                {"name": "uuidField", "type": "fixed", "size": 16, 
"logicalType": "uuid"},
-                {"name": "innerRecord", "type": ["null", "TestRecord"]}
-            ]
-        }"#,
-        )?;
-
-        #[derive(Serialize)]
-        #[serde(rename_all = "camelCase")]
-        struct TestRecord {
-            string_field: String,
-            int_field: i32,
-            big_decimal_field: BigDecimal,
-            uuid_field: Uuid,
-            // #[serde(skip_serializing_if = "Option::is_none")] => Never 
ignore None!
-            inner_record: Option<Box<TestRecord>>,
-        }
-
-        assert!(!crate::util::is_human_readable());
-        let mut buffer: Vec<u8> = Vec::new();
-        let rs = ResolvedSchema::try_from(&schema)?;
-        let mut serializer =
-            SchemaAwareWriteSerializer::new(&mut buffer, &schema, 
rs.get_names(), None);
-
-        let good_record = TestRecord {
-            string_field: String::from("test"),
-            int_field: 10,
-            big_decimal_field: BigDecimal::new(BigInt::new(Sign::Plus, 
vec![50024]), 2),
-            uuid_field: 
"8c28da81-238c-4326-bddd-4e3d00cc5098".parse::<Uuid>()?,
-            inner_record: Some(Box::new(TestRecord {
-                string_field: String::from("inner_test"),
-                int_field: 100,
-                big_decimal_field: BigDecimal::new(BigInt::new(Sign::Plus, 
vec![20038]), 2),
-                uuid_field: 
"8c28da81-238c-4326-bddd-4e3d00cc5099".parse::<Uuid>()?,
-                inner_record: None,
-            })),
-        };
-        good_record.serialize(&mut serializer)?;
-
-        assert_eq!(
-            buffer.as_slice(),
-            &[
-                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
-            ]
-        );
-
-        Ok(())
-    }
-
     #[test]
     fn avro_rs_337_serialize_union_record_variant() -> TestResult {
         let schema = Schema::parse_str(
diff --git a/avro/src/serde/ser_schema2/mod.rs 
b/avro/src/serde/ser_schema2/mod.rs
index a68f912..0d656a7 100644
--- a/avro/src/serde/ser_schema2/mod.rs
+++ b/avro/src/serde/ser_schema2/mod.rs
@@ -12,6 +12,7 @@ use crate::serde::ser_schema2::map::MapSerializer;
 use crate::serde::ser_schema2::record::RecordSerializer;
 use crate::serde::ser_schema2::tuple::TupleSerializer;
 use crate::serde::ser_schema2::union::UnionAwareSerializer;
+use crate::serde::with::{BytesType, SER_BYTES_TYPE};
 use crate::{Error, Schema};
 use serde::ser::SerializeMap;
 use serde::{Serialize, Serializer};
@@ -120,7 +121,7 @@ impl<'s, 'w, W: Write> SchemaAwareSerializer<'s, 'w, W> {
         })
     }
 
-    fn serialize_int(mut self, original_ty: &'static str, v: i32) -> 
Result<usize, Error> {
+    fn serialize_int(self, original_ty: &'static str, v: i32) -> Result<usize, 
Error> {
         match self.schema {
             Schema::Int | Schema::Date | Schema::TimeMillis => encode_int(v, 
&mut *self.writer),
             _ => Err(self.error(
@@ -130,7 +131,7 @@ impl<'s, 'w, W: Write> SchemaAwareSerializer<'s, 'w, W> {
         }
     }
 
-    fn serialize_long(mut self, original_ty: &'static str, v: i64) -> 
Result<usize, Error> {
+    fn serialize_long(self, original_ty: &'static str, v: i64) -> 
Result<usize, Error> {
         match self.schema {
             Schema::Long | Schema::TimeMicros | Schema::TimestampMillis | 
Schema::TimestampMicros
             | Schema::TimestampNanos | Schema::LocalTimestampMillis | 
Schema::LocalTimestampMicros
@@ -171,7 +172,7 @@ impl<'s, 'w, W: Write> Serializer for 
SchemaAwareSerializer<'s, 'w, W> {
     type SerializeStruct = RecordSerializer<'s, 'w, W>;
     type SerializeStructVariant = RecordSerializer<'s, 'w, W>;
 
-    fn serialize_bool(mut self, v: bool) -> Result<Self::Ok, Self::Error> {
+    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
         let Schema::Boolean = self.schema else {
             return Err(self.error("bool", "Expected Schema::Boolean"));
         };
@@ -277,22 +278,22 @@ impl<'s, 'w, W: Write> Serializer for 
SchemaAwareSerializer<'s, 'w, W> {
     }
 
     fn serialize_bytes(mut self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
-        match self.schema {
-            Schema::Bytes | Schema::BigDecimal | Schema::Decimal(DecimalSchema 
{ inner: InnerDecimalSchema::Bytes, .. }) => {
+        match (SER_BYTES_TYPE.get(), self.schema) {
+            (BytesType::Unset | BytesType::Bytes, Schema::Bytes | 
Schema::BigDecimal | Schema::Decimal(DecimalSchema { inner: 
InnerDecimalSchema::Bytes, .. })) => {
                 self.write_bytes_with_len(v)
             }
-            Schema::Fixed(fixed) | Schema::Decimal(DecimalSchema { inner: 
InnerDecimalSchema::Fixed(fixed), ..}) => {
+            (BytesType::Unset | BytesType::Fixed, Schema::Fixed(fixed) | 
Schema::Decimal(DecimalSchema { inner: InnerDecimalSchema::Fixed(fixed), ..}) | 
Schema::Uuid(UuidSchema::Fixed(fixed)) | Schema::Duration(fixed)) => {
                 if fixed.size != v.len() {
                     Err(self.error("bytes", format!("Fixed size ({}) does not 
match value length ({})", fixed.size, v.len())))
                 } else {
                     self.write_bytes(v)
                 }
             }
-            _ => Err(self.error("bytes", "Expected Schema::Bytes | 
Schema::Uuid(Fixed) | Schema::BigDecimal | Schema::Decimal")),
+            _ => Err(self.error("bytes", "Expected Schema::Bytes | 
Schema::Uuid(Fixed) | Schema::BigDecimal | Schema::Decimal | 
Schema::Duration")),
         }
     }
 
-    fn serialize_none(mut self) -> Result<Self::Ok, Self::Error> {
+    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
         let Schema::Union(union) = self.schema else {
             return Err(self.error("None", "Expected Schema::Union([null, 
_])"));
         };
@@ -305,18 +306,18 @@ impl<'s, 'w, W: Write> Serializer for 
SchemaAwareSerializer<'s, 'w, W> {
         encode_int(index as i32, &mut *self.writer)
     }
 
-    fn serialize_some<T>(mut self, value: &T) -> Result<Self::Ok, Self::Error>
+    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
     where
         T: ?Sized + Serialize,
     {
         let Schema::Union(union) = self.schema else {
-            return Err(self.error("None", "Expected Schema::Union([null, 
_])"));
+            return Err(self.error("Some", "Expected Schema::Union([null, 
_])"));
         };
         if union.variants().len() != 2 {
-            return Err(self.error("None", "Expected Schema::Union([null, 
_])"));
+            return Err(self.error("Some", "Expected Schema::Union([null, 
_])"));
         }
         let Some(index) = union.index().get(&SchemaKind::Null).copied() else {
-            return Err(self.error("None", "Expected Schema::Union([null, 
_])"));
+            return Err(self.error("Some", "Expected Schema::Union([null, 
_])"));
         };
         // Convert the index of null to the other index
         let index = (index + 1) & 1;
@@ -328,7 +329,7 @@ impl<'s, 'w, W: Write> Serializer for 
SchemaAwareSerializer<'s, 'w, W> {
 
     fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
         let Schema::Null = self.schema else {
-            return Err(self.error("()", "Expected Schema::Null"));
+            return Err(self.error("unit", "Expected Schema::Null"));
         };
         Ok(0)
     }
@@ -350,7 +351,7 @@ impl<'s, 'w, W: Write> Serializer for 
SchemaAwareSerializer<'s, 'w, W> {
     }
 
     fn serialize_unit_variant(
-        mut self,
+        self,
         name: &'static str,
         variant_index: u32,
         variant: &'static str,
@@ -460,7 +461,7 @@ impl<'s, 'w, W: Write> Serializer for 
SchemaAwareSerializer<'s, 'w, W> {
     }
 
     fn serialize_newtype_variant<T>(
-        mut self,
+        self,
         name: &'static str,
         variant_index: u32,
         variant: &'static str,
@@ -582,7 +583,7 @@ impl<'s, 'w, W: Write> Serializer for 
SchemaAwareSerializer<'s, 'w, W> {
     }
 
     fn serialize_tuple_variant(
-        mut self,
+        self,
         name: &'static str,
         variant_index: u32,
         variant: &'static str,
@@ -728,7 +729,7 @@ impl<'s, 'w, W: Write> Serializer for 
SchemaAwareSerializer<'s, 'w, W> {
     }
 
     fn serialize_struct_variant(
-        mut self,
+        self,
         name: &'static str,
         variant_index: u32,
         variant: &'static str,
@@ -936,7 +937,11 @@ mod tests {
 
         4u8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
         31u16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        13u32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
+        assert!(
+            13u32
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
         7i8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
         (-57i16).serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
         129i32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
@@ -950,7 +955,7 @@ mod tests {
                 .is_err()
         );
 
-        assert_eq!(buffer.as_slice(), &[8, 62, 26, 14, 113, 130, 2]);
+        assert_eq!(buffer.as_slice(), &[8, 62, 14, 113, 130, 2]);
 
         Ok(())
     }
@@ -965,13 +970,35 @@ mod tests {
             human_readable: false,
         };
 
-        4u8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        31u16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
+        assert!(
+            4u8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
+        assert!(
+            31u16
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
         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)?)?;
+        assert!(
+            291u64
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
+        assert!(
+            7i8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
+        assert!(
+            (-57i16)
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
+        assert!(
+            129i32
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
         (-432i64).serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
         assert!(
             "".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
@@ -983,10 +1010,7 @@ mod tests {
                 .is_err()
         );
 
-        assert_eq!(
-            buffer.as_slice(),
-            &[8, 62, 26, 198, 4, 14, 113, 130, 2, 223, 6]
-        );
+        assert_eq!(buffer.as_slice(), &[26, 223, 6]);
 
         Ok(())
     }
@@ -1002,7 +1026,11 @@ mod tests {
         };
 
         4.7f32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        (-14.1f64).serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
+        assert!(
+            (-14.1f64)
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
         assert!(
             "".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
                 .is_err()
@@ -1013,7 +1041,7 @@ mod tests {
                 .is_err()
         );
 
-        assert_eq!(buffer.as_slice(), &[102, 102, 150, 64, 154, 153, 97, 193]);
+        assert_eq!(buffer.as_slice(), &[102, 102, 150, 64]);
 
         Ok(())
     }
@@ -1029,7 +1057,12 @@ mod tests {
         };
 
         4.7f32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        (-14.1f64).serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
+        (-14.1f32).serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
+        assert!(
+            (-14.1f64)
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
         assert!(
             "".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
                 .is_err()
@@ -1055,8 +1088,15 @@ mod tests {
             human_readable: false,
         };
 
-        'a'.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        "test".serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
+        assert!(
+            'a'.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
+        assert!(
+            "test"
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
         Bytes::new(&[12, 3, 7, 91, 4]).serialize(SchemaAwareSerializer::new(
             &mut buffer,
             &schema,
@@ -1072,10 +1112,7 @@ mod tests {
                 .is_err()
         );
 
-        assert_eq!(
-            buffer.as_slice(),
-            &[2, b'a', 8, b't', b'e', b's', b't', 10, 12, 3, 7, 91, 4]
-        );
+        assert_eq!(buffer.as_slice(), &[10, 12, 3, 7, 91, 4]);
 
         Ok(())
     }
@@ -1092,11 +1129,11 @@ mod tests {
 
         '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,
-            &schema,
-            config,
-        )?)?;
+        assert!(
+            Bytes::new(&[12, 3, 7, 91, 4])
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config,)?)
+                .is_err()
+        );
         assert!(
             ().serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
                 .is_err()
@@ -1107,10 +1144,7 @@ mod tests {
                 .is_err()
         );
 
-        assert_eq!(
-            buffer.as_slice(),
-            &[2, b'a', 8, b't', b'e', b's', b't', 10, 12, 3, 7, 91, 4]
-        );
+        assert_eq!(buffer.as_slice(), &[2, b'a', 8, b't', b'e', b's', b't']);
 
         Ok(())
     }
@@ -1129,14 +1163,14 @@ 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,
@@ -1202,6 +1236,7 @@ mod tests {
         EmptyRecord.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
 
         #[derive(Serialize)]
+        #[serde(rename = "EmptyRecord")]
         struct NonEmptyRecord {
             foo: String,
         }
@@ -1222,8 +1257,8 @@ mod tests {
                 value,
                 schema,
             }) => {
-                assert_eq!(value_type, "none"); // serialize_unit() delegates 
to serialize_none()
-                assert_eq!(value, "None. Cause: Expected: Record. Got: Null");
+                assert_eq!(value_type, "unit");
+                assert_eq!(value, "Expected Schema::Null");
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
@@ -1245,6 +1280,7 @@ mod tests {
         )?;
 
         #[derive(Serialize)]
+        #[serde(rename_all = "UPPERCASE")]
         enum Suit {
             Spades,
             Hearts,
@@ -1272,8 +1308,8 @@ mod tests {
                 value,
                 schema,
             }) => {
-                assert_eq!(value_type, "none");
-                assert_eq!(value, "None. Cause: Expected: Enum. Got: Null");
+                assert_eq!(value_type, "None");
+                assert_eq!(value, "Expected Schema::Union([null, _])");
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
@@ -1313,13 +1349,15 @@ mod tests {
                 schema,
             }) => {
                 assert_eq!(value_type, "f32");
-                assert_eq!(value, "1. Cause: Expected: Long. Got: Float");
+                assert_eq!(value, "Expected Schema::Float");
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
         }
 
-        assert_eq!(buffer.as_slice(), &[6, 20, 10, 160, 6, 0]);
+        // The 2 at the end is because the DirectArraySerializer immediately 
writes the length and doesn't
+        // know the values will be invalid yet
+        assert_eq!(buffer.as_slice(), &[6, 20, 10, 160, 6, 0, 2]);
 
         Ok(())
     }
@@ -1357,18 +1395,20 @@ mod tests {
                 value,
                 schema,
             }) => {
-                assert_eq!(value_type, "string");
-                assert_eq!(value, "value1. Cause: Expected: Long. Got: 
String");
+                assert_eq!(value_type, "str");
+                assert_eq!(value, "Expected Schema::String | 
Schema::Uuid(String)");
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
         }
 
+        // The last 7 bytes are written because the DirectMapSerializer will 
write immediately and doesn't
+        // know yet that the value is not a long
         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
+                0, 2, 10, 105, 116, 101, 109, 49
             ]
         );
 
@@ -1414,11 +1454,8 @@ mod tests {
                 value,
                 schema,
             }) => {
-                assert_eq!(value_type, "string");
-                assert_eq!(
-                    value,
-                    "invalid. Cause: Expected one of the union variants [Null, 
Long]. Got: String"
-                );
+                assert_eq!(value_type, "str");
+                assert_eq!(value, "Expected Schema::String | 
Schema::Uuid(String)");
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
@@ -1473,10 +1510,7 @@ mod tests {
                 schema,
             }) => {
                 assert_eq!(value_type, "f64");
-                assert_eq!(
-                    value,
-                    "1. Cause: Cannot find a Double schema in [Null, Long, 
String]"
-                );
+                assert_eq!(value, "Expected Schema::Double");
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
@@ -1526,8 +1560,8 @@ mod tests {
                 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
+                    "Fixed size (8) does not match value length (1)"
+                );
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
@@ -1543,8 +1577,9 @@ mod tests {
                 value,
                 schema,
             }) => {
-                assert_eq!(value_type, "tuple"); // TODO: why is this 'tuple' 
?!
-                assert_eq!(value, "tuple (len=8). Cause: Expected: Fixed. Got: 
Array");
+                // Arrays are serialized as a tuple by Serde
+                assert_eq!(value_type, "tuple");
+                assert_eq!(value, "Expected Schema::Record(fields.len() == 
8)");
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
@@ -1560,8 +1595,9 @@ mod tests {
                 value,
                 schema,
             }) => {
-                assert_eq!(*value_type, "tuple"); // TODO: why is this 'tuple' 
?!
-                assert_eq!(value, "tuple (len=8). Cause: Expected: Fixed. Got: 
Array");
+                // This is a tuple as Serde serializes array as a tuple
+                assert_eq!(*value_type, "tuple");
+                assert_eq!(value, "Expected Schema::Record(fields.len() == 
8)");
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
@@ -1599,8 +1635,8 @@ mod tests {
                 value,
                 schema,
             }) => {
-                assert_eq!(value_type, "none");
-                assert_eq!(value, "None. Cause: Expected: Decimal. Got: Null");
+                assert_eq!(value_type, "unit");
+                assert_eq!(value, "Expected Schema::Null");
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
@@ -1640,8 +1676,8 @@ mod tests {
                 value,
                 schema,
             }) => {
-                assert_eq!(value_type, "none");
-                assert_eq!(value, "None. Cause: Expected: Decimal. Got: Null");
+                assert_eq!(value_type, "unit");
+                assert_eq!(value, "Expected Schema::Null");
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
@@ -1669,7 +1705,10 @@ mod tests {
         };
 
         let val = BigDecimal::new(BigInt::new(Sign::Plus, vec![50024]), 2);
-        val.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
+        crate::serde::bigdecimal::serialize(
+            &val,
+            SchemaAwareSerializer::new(&mut buffer, &schema, config)?,
+        )?;
 
         assert_eq!(buffer.as_slice(), &[10, 6, 0, 195, 104, 4]);
 
@@ -1709,7 +1748,10 @@ mod tests {
                 schema,
             }) => {
                 assert_eq!(value_type, "u8");
-                assert_eq!(value, "1. Cause: Expected: Uuid. Got: Int");
+                assert_eq!(
+                    value,
+                    "Expected Schema::Int | Schema::Date | Schema::TimeMillis"
+                );
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
@@ -1743,7 +1785,11 @@ mod tests {
 
         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)?)?;
+        assert!(
+            10000_u32
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
         1000_i16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
         10000_i32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
 
@@ -1757,7 +1803,7 @@ mod tests {
                 schema,
             }) => {
                 assert_eq!(value_type, "f32");
-                assert_eq!(value, "10000. Cause: Expected: Date. Got: Float");
+                assert_eq!(value, "Expected Schema::Float");
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
@@ -1765,7 +1811,7 @@ mod tests {
 
         assert_eq!(
             buffer.as_slice(),
-            &[200, 1, 208, 15, 160, 156, 1, 208, 15, 160, 156, 1]
+            &[200, 1, 208, 15, 208, 15, 160, 156, 1]
         );
 
         Ok(())
@@ -1789,7 +1835,11 @@ mod tests {
 
         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)?)?;
+        assert!(
+            10000_u32
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
         1000_i16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
         10000_i32.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
 
@@ -1803,16 +1853,13 @@ mod tests {
                 schema,
             }) => {
                 assert_eq!(value_type, "f32");
-                assert_eq!(value, "10000. Cause: Expected: TimeMillis. Got: 
Float");
+                assert_eq!(value, "Expected Schema::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_eq!(buffer.as_slice(), &[200, 1, 208, 15, 208, 15, 160, 156, 
1]);
 
         Ok(())
     }
@@ -1833,11 +1880,27 @@ mod tests {
             human_readable: false,
         };
 
-        100_u8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-        1000_u16.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
+        assert!(
+            100_u8
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
+        assert!(
+            1000_u16
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
         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)?)?;
+        assert!(
+            1000_i16
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
+        assert!(
+            10000_i32
+                .serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)
+                .is_err()
+        );
         10000_i64.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
 
         match 10000_f32
@@ -1850,18 +1913,13 @@ mod tests {
                 schema,
             }) => {
                 assert_eq!(value_type, "f32");
-                assert_eq!(value, "10000. Cause: Expected: TimeMicros. Got: 
Float");
+                assert_eq!(value, "Expected Schema::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_eq!(buffer.as_slice(), &[160, 156, 1, 160, 156, 1,]);
 
         Ok(())
     }
@@ -1883,11 +1941,27 @@ mod tests {
                 human_readable: false,
             };
 
-            100_u8.serialize(SchemaAwareSerializer::new(&mut buffer, &schema, 
config)?)?;
-            1000_u16.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
+            assert!(
+                100_u8
+                    .serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)
+                    .is_err()
+            );
+            assert!(
+                1000_u16
+                    .serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)
+                    .is_err()
+            );
             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)?)?;
+            assert!(
+                1000_i16
+                    .serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)
+                    .is_err()
+            );
+            assert!(
+                10000_i32
+                    .serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)
+                    .is_err()
+            );
             10000_i64.serialize(SchemaAwareSerializer::new(&mut buffer, 
&schema, config)?)?;
 
             match 10000_f64
@@ -1904,23 +1978,13 @@ mod tests {
                         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!(value, format!("Expected Schema::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_eq!(buffer.as_slice(), &[160, 156, 1, 160, 156, 1,]);
         }
 
         Ok(())
@@ -1957,11 +2021,9 @@ mod tests {
                 value,
                 schema,
             }) => {
-                assert_eq!(value_type, "tuple"); // TODO: why is this 'tuple' 
?!
-                assert_eq!(
-                    value,
-                    "tuple (len=12). Cause: Expected: Duration. Got: Array"
-                );
+                // This is a tuple because Serde serializes arrays [T; N] as 
tuples
+                assert_eq!(value_type, "tuple");
+                assert_eq!(value, "Expected Schema::Record(fields.len() == 
12)");
                 assert_eq!(schema, schema);
             }
             unexpected => panic!("Expected an error. Got: {unexpected:?}"),
@@ -1993,6 +2055,7 @@ mod tests {
         struct TestRecord {
             string_field: String,
             int_field: i32,
+            #[serde(with = "crate::serde::bigdecimal")]
             big_decimal_field: BigDecimal,
             uuid_field: Uuid,
             // #[serde(skip_serializing_if = "Option::is_none")] => Never 
ignore None!
diff --git a/avro/src/serde/ser_schema2/record.rs 
b/avro/src/serde/ser_schema2/record.rs
index 9f523f1..3ac2845 100644
--- a/avro/src/serde/ser_schema2/record.rs
+++ b/avro/src/serde/ser_schema2/record.rs
@@ -63,8 +63,7 @@ impl<'s, 'w, W: Write> RecordSerializer<'s, 'w, W> {
                 // Current field position is smaller than this field position,
                 // so we're still missing at least one field, save this field 
temporarily
                 let mut bytes = Vec::new();
-                let value_ser =
-                    SchemaAwareSerializer::new(&mut *self.writer, 
&field.schema, self.config)?;
+                let value_ser = SchemaAwareSerializer::new(&mut bytes, 
&field.schema, self.config)?;
                 value.serialize(value_ser)?;
                 if self.field_cache.insert(field.position, bytes).is_some() {
                     Err(Details::FieldNameDuplicate(field.name.clone()).into())
diff --git a/avro/src/serde/ser_schema2/union.rs 
b/avro/src/serde/ser_schema2/union.rs
index aadbb38..e2f46c7 100644
--- a/avro/src/serde/ser_schema2/union.rs
+++ b/avro/src/serde/ser_schema2/union.rs
@@ -6,6 +6,7 @@ use crate::serde::ser_schema2::map::MapSerializer;
 use crate::serde::ser_schema2::record::RecordSerializer;
 use crate::serde::ser_schema2::tuple::TupleSerializer;
 use crate::serde::ser_schema2::{Config, SchemaAwareSerializer};
+use crate::serde::with::{BytesType, SER_BYTES_TYPE};
 use crate::{Error, Schema};
 use serde::{Serialize, Serializer};
 use std::io::Write;
@@ -34,7 +35,7 @@ impl<'s, 'w, W: Write> UnionAwareSerializer<'s, 'w, W> {
         })
     }
 
-    fn serialize_int(mut self, original_ty: &'static str, v: i32) -> 
Result<usize, Error> {
+    fn serialize_int(self, original_ty: &'static str, v: i32) -> Result<usize, 
Error> {
         let Some(index) = 
self.union_schema.index().get(&SchemaKind::Int).copied() else {
             return Err(self.error(
                 original_ty,
@@ -46,7 +47,7 @@ impl<'s, 'w, W: Write> UnionAwareSerializer<'s, 'w, W> {
         Ok(bytes_written)
     }
 
-    fn serialize_long(mut self, original_ty: &'static str, v: i64) -> 
Result<usize, Error> {
+    fn serialize_long(self, original_ty: &'static str, v: i64) -> 
Result<usize, Error> {
         let Some(index) = 
self.union_schema.index().get(&SchemaKind::Long).copied() else {
             return Err(self.error(original_ty, "Expected Schema::Long | 
Schema::TimeMicros | Schema::{,Local}Timestamp{Millis,Micros,Nanos} in 
variants"));
         };
@@ -75,7 +76,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
     type SerializeStruct = RecordSerializer<'s, 'w, W>;
     type SerializeStructVariant = RecordSerializer<'s, 'w, W>;
 
-    fn serialize_bool(mut self, v: bool) -> Result<Self::Ok, Self::Error> {
+    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
         let Some(index) = 
self.union_schema.index().get(&SchemaKind::Boolean).copied() else {
             return Err(self.error("bool", "Expected Schema::Boolean in 
variants"));
         };
@@ -103,7 +104,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
         self.serialize_long("i64", v)
     }
 
-    fn serialize_i128(mut self, v: i128) -> Result<Self::Ok, Self::Error> {
+    fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> {
         let Some((index, schema)) = self.find_named_schema("i128") else {
             return Err(self.error(
                 "i128",
@@ -128,7 +129,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
         self.serialize_long("u32", i64::from(v))
     }
 
-    fn serialize_u64(mut self, v: u64) -> Result<Self::Ok, Self::Error> {
+    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
         let Some((index, schema)) = self.find_named_schema("u64") else {
             return Err(self.error(
                 "u64",
@@ -141,7 +142,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
         Ok(bytes_written)
     }
 
-    fn serialize_u128(mut self, v: u128) -> Result<Self::Ok, Self::Error> {
+    fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {
         let Some((index, schema)) = self.find_named_schema("u128") else {
             return Err(self.error(
                 "i128",
@@ -154,7 +155,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
         Ok(bytes_written)
     }
 
-    fn serialize_f32(mut self, v: f32) -> Result<Self::Ok, Self::Error> {
+    fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
         let Some(index) = 
self.union_schema.index().get(&SchemaKind::Float).copied() else {
             return Err(self.error("f32", "Expected Schema::Float in 
variants"));
         };
@@ -164,7 +165,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
         Ok(bytes_written)
     }
 
-    fn serialize_f64(mut self, v: f64) -> Result<Self::Ok, Self::Error> {
+    fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
         let Some(index) = 
self.union_schema.index().get(&SchemaKind::Double).copied() else {
             return Err(self.error("f64", "Expected Schema::Double in 
variants"));
         };
@@ -174,7 +175,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
         Ok(bytes_written)
     }
 
-    fn serialize_char(mut self, v: char) -> Result<Self::Ok, Self::Error> {
+    fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
         let Some(index) = 
self.union_schema.index().get(&SchemaKind::String).copied() else {
             return Err(self.error("char", "Expected Schema::String in 
variants"));
         };
@@ -184,7 +185,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
         Ok(bytes_written)
     }
 
-    fn serialize_str(mut self, v: &str) -> Result<Self::Ok, Self::Error> {
+    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
         let Some(index) = 
self.union_schema.index().get(&SchemaKind::String).copied() else {
             return Err(self.error("str", "Expected Schema::String in 
variants"));
         };
@@ -194,39 +195,71 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
         Ok(bytes_written)
     }
 
-    fn serialize_bytes(mut self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
-        let potential_bytes_index = 
self.union_schema.index().get(&SchemaKind::Bytes).copied();
-        let potential_fixed_index =
-            self.union_schema
-                .variants()
-                .iter()
-                .enumerate()
-                .find(|(_i, s)| {
-                    if let Schema::Fixed(f) = s {
-                        f.size == v.len()
-                    } else {
-                        false
+    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
+        let (index, schema) = match SER_BYTES_TYPE.get() {
+            BytesType::Bytes => {
+                let Some(index) = 
self.union_schema.index().get(&SchemaKind::Bytes).copied() else {
+                    return Err(self.error("bytes", "Expected Schema::Bytes in 
variants"));
+                };
+                (index, &Schema::Bytes)
+            }
+            BytesType::Fixed => {
+                let Some((index, schema)) =
+                    self.union_schema
+                        .variants()
+                        .iter()
+                        .enumerate()
+                        .find(|(_i, s)| {
+                            if let Schema::Fixed(f) = s {
+                                f.size == v.len()
+                            } else {
+                                false
+                            }
+                        })
+                else {
+                    return Err(self.error(
+                        "bytes",
+                        format!("Expected Schema::Fixed(size = {}) in 
variants", v.len()),
+                    ));
+                };
+                (index, schema)
+            }
+            BytesType::Unset => {
+                let potential_bytes_index =
+                    self.union_schema.index().get(&SchemaKind::Bytes).copied();
+                let potential_fixed_index =
+                    self.union_schema
+                        .variants()
+                        .iter()
+                        .enumerate()
+                        .find(|(_i, s)| {
+                            if let Schema::Fixed(f) = s {
+                                f.size == v.len()
+                            } else {
+                                false
+                            }
+                        });
+                match (potential_bytes_index, potential_fixed_index) {
+                    (Some(bytes_index), Some((fixed_index, fixed_schema))) => {
+                        if bytes_index < fixed_index {
+                            (bytes_index, &Schema::Bytes)
+                        } else {
+                            (fixed_index, fixed_schema)
+                        }
+                    }
+                    (Some(bytes_index), None) => (bytes_index, &Schema::Bytes),
+                    (None, Some((fixed_index, fixed_schema))) => (fixed_index, 
fixed_schema),
+                    (None, None) => {
+                        return Err(self.error(
+                            "bytes",
+                            format!(
+                                "Expected Schema::Bytes or Schema::Fixed(size: 
{}) in variants",
+                                v.len()
+                            ),
+                        ));
                     }
-                });
-        let (index, schema) = match (potential_bytes_index, 
potential_fixed_index) {
-            (Some(bytes_index), Some((fixed_index, fixed_schema))) => {
-                if bytes_index < fixed_index {
-                    (bytes_index, &Schema::Bytes)
-                } else {
-                    (fixed_index, fixed_schema)
                 }
             }
-            (Some(bytes_index), None) => (bytes_index, &Schema::Bytes),
-            (None, Some((fixed_index, fixed_schema))) => (fixed_index, 
fixed_schema),
-            (None, None) => {
-                return Err(self.error(
-                    "bytes",
-                    format!(
-                        "Expected Schema::Bytes or Schema::Fixed(size: {}) in 
variants",
-                        v.len()
-                    ),
-                ));
-            }
         };
         let mut bytes_written = encode_int(index as i32, &mut *self.writer)?;
         let ser = SchemaAwareSerializer::new(self.writer, schema, 
self.config)?;
@@ -269,7 +302,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
     }
 
     fn serialize_unit_variant(
-        mut self,
+        self,
         name: &'static str,
         variant_index: u32,
         variant: &'static str,
@@ -287,7 +320,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
     }
 
     fn serialize_newtype_struct<T>(
-        mut self,
+        self,
         name: &'static str,
         value: &T,
     ) -> Result<Self::Ok, Self::Error>
@@ -308,7 +341,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
     }
 
     fn serialize_newtype_variant<T>(
-        mut self,
+        self,
         name: &'static str,
         variant_index: u32,
         variant: &'static str,
@@ -339,7 +372,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
         }
     }
 
-    fn serialize_seq(mut self, len: Option<usize>) -> 
Result<Self::SerializeSeq, Self::Error> {
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, 
Self::Error> {
         let Some(index) = 
self.union_schema.index().get(&SchemaKind::Array).copied() else {
             return Err(self.error("seq", "Expected Schema::Array in 
variants"));
         };
@@ -356,7 +389,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
         )?)
     }
 
-    fn serialize_tuple(mut self, len: usize) -> Result<Self::SerializeTuple, 
Self::Error> {
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, 
Self::Error> {
         let Some((index, Schema::Record(schema))) = self
             .union_schema
             .variants()
@@ -387,7 +420,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
     }
 
     fn serialize_tuple_struct(
-        mut self,
+        self,
         name: &'static str,
         len: usize,
     ) -> Result<Self::SerializeTupleStruct, Self::Error> {
@@ -410,7 +443,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
     }
 
     fn serialize_tuple_variant(
-        mut self,
+        self,
         name: &'static str,
         variant_index: u32,
         variant: &'static str,
@@ -467,7 +500,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
         }
     }
 
-    fn serialize_map(mut self, len: Option<usize>) -> 
Result<Self::SerializeMap, Self::Error> {
+    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, 
Self::Error> {
         let Some(index) = 
self.union_schema.index().get(&SchemaKind::Map).copied() else {
             return Err(self.error("map", "Expected Schema::Map in variants"));
         };
@@ -485,7 +518,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
     }
 
     fn serialize_struct(
-        mut self,
+        self,
         name: &'static str,
         _len: usize,
     ) -> Result<Self::SerializeStruct, Self::Error> {
@@ -506,7 +539,7 @@ impl<'s, 'w, W: Write> Serializer for 
UnionAwareSerializer<'s, 'w, W> {
     }
 
     fn serialize_struct_variant(
-        mut self,
+        self,
         name: &'static str,
         variant_index: u32,
         variant: &'static str,
diff --git a/avro/src/serde/with.rs b/avro/src/serde/with.rs
index d5da690..4fe13b1 100644
--- a/avro/src/serde/with.rs
+++ b/avro/src/serde/with.rs
@@ -22,7 +22,7 @@ thread_local! {
     /// [`Value::Bytes`] or [`Value::Fixed`].
     ///
     /// Relies on the fact that serde's serialization process is 
single-threaded.
-    pub(crate) static SER_BYTES_TYPE: Cell<BytesType> = const { 
Cell::new(BytesType::Bytes) };
+    pub(crate) static SER_BYTES_TYPE: Cell<BytesType> = const { 
Cell::new(BytesType::Unset) };
 
     /// A thread local that is used to decide if a [`Value::Bytes`] needs to 
be deserialized to
     /// a [`Vec`] or slice.
@@ -33,6 +33,7 @@ thread_local! {
 
 #[derive(Debug, Clone, Copy)]
 pub(crate) enum BytesType {
+    Unset,
     Bytes,
     Fixed,
 }
@@ -99,6 +100,7 @@ pub mod bytes {
     use crate::{
         Schema,
         schema::{Name, Namespace, RecordField},
+        serde::with::BytesType,
     };
 
     /// Returns [`Schema::Bytes`]
@@ -119,6 +121,7 @@ pub mod bytes {
     where
         S: Serializer,
     {
+        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
         serde_bytes::serialize(bytes, serializer)
     }
 
@@ -126,6 +129,7 @@ pub mod bytes {
     where
         D: Deserializer<'de>,
     {
+        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
         serde_bytes::deserialize(deserializer)
     }
 }
@@ -161,6 +165,7 @@ pub mod bytes_opt {
     use crate::{
         Schema,
         schema::{Name, Namespace, RecordField, UnionSchema},
+        serde::with::BytesType,
     };
 
     /// Returns `Schema::Union(Schema::Null, Schema::Bytes)`
@@ -184,6 +189,7 @@ pub mod bytes_opt {
         S: Serializer,
         B: Borrow<[u8]> + serde_bytes::Serialize,
     {
+        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
         serde_bytes::serialize(bytes, serializer)
     }
 
@@ -191,6 +197,7 @@ pub mod bytes_opt {
     where
         D: Deserializer<'de>,
     {
+        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
         serde_bytes::deserialize(deserializer)
     }
 }
@@ -268,6 +275,7 @@ pub mod fixed {
     where
         D: Deserializer<'de>,
     {
+        let _guard = super::BytesTypeGuard::set(BytesType::Fixed);
         serde_bytes::deserialize(deserializer)
     }
 }
@@ -342,6 +350,7 @@ pub mod fixed_opt {
     where
         D: Deserializer<'de>,
     {
+        let _guard = super::BytesTypeGuard::set(BytesType::Fixed);
         serde_bytes::deserialize(deserializer)
     }
 }
@@ -380,6 +389,7 @@ pub mod slice {
     use crate::{
         Schema,
         schema::{Name, Namespace, RecordField},
+        serde::with::BytesType,
     };
 
     /// Returns [`Schema::Bytes`]
@@ -400,6 +410,7 @@ pub mod slice {
     where
         S: Serializer,
     {
+        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
         serde_bytes::serialize(bytes, serializer)
     }
 
@@ -407,6 +418,7 @@ pub mod slice {
     where
         D: Deserializer<'de>,
     {
+        let _bytes_guard = super::BytesTypeGuard::set(BytesType::Bytes);
         let _guard = super::BorrowedGuard::set(true);
         serde_bytes::deserialize(deserializer)
     }
@@ -445,6 +457,7 @@ pub mod slice_opt {
     use crate::{
         Schema,
         schema::{Name, Namespace, RecordField, UnionSchema},
+        serde::with::BytesType,
     };
 
     /// Returns `Schema::Union(Schema::Null, Schema::Bytes)`
@@ -468,6 +481,7 @@ pub mod slice_opt {
         S: Serializer,
         B: Borrow<[u8]> + serde_bytes::Serialize,
     {
+        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
         serde_bytes::serialize(&bytes, serializer)
     }
 
@@ -475,11 +489,317 @@ pub mod slice_opt {
     where
         D: Deserializer<'de>,
     {
+        let _bytes_guard = super::BytesTypeGuard::set(BytesType::Bytes);
         let _guard = super::BorrowedGuard::set(true);
         serde_bytes::deserialize(deserializer)
     }
 }
 
+/// Efficient (de)serialization of [`BigDecimal`] values.
+///
+/// This module is intended to be used through the Serde `with` attribute.
+///
+/// Use [`apache_avro::serde::bigdecimal_opt`] for optional decimal 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)]
+///     #[serde(with = "apache_avro::serde::bigdecimal")]
+///     decimal: BigDecimal,
+/// }
+/// ```
+///
+/// [`BigDecimal`]: bigdecimal::BigDecimal
+/// [`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, Namespace, RecordField},
+        serde::with::BytesType,
+    };
+
+    /// Returns [`Schema::BigDecimal`]
+    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: &Namespace) -> Schema {
+        Schema::BigDecimal
+    }
+
+    /// Returns `None`
+    pub fn get_record_fields_in_ctxt(
+        _: usize,
+        _: &mut HashSet<Name>,
+        _: &Namespace,
+    ) -> 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(|e| 
S::Error::custom(e))?;
+        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);
+        let bytes: &'de [u8] = serde_bytes::deserialize(deserializer)?;
+
+        deserialize_big_decimal(bytes).map_err(|e| D::Error::custom(e))
+    }
+}
+
+/// Efficient (de)serialization of optional [`BigDecimal`] values.
+///
+/// This module is intended to be used through the Serde `with` attribute.
+///
+/// Use [`apache_avro::serde::bigdecimal`] for non-optional decimal 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)]
+///     #[serde(with = "apache_avro::serde::bigdecimal_opt")]
+///     decimal: Option<BigDecimal>,
+/// }
+/// ```
+///
+/// [`BigDecimal`]: bigdecimal::BigDecimal
+/// [`apache_avro::serde::bigdecimal`]: bigdecimal
+pub mod bigdecimal_opt {
+    use bigdecimal::BigDecimal;
+    use serde::{Deserializer, Serializer, de::Error as _, ser::Error as _};
+    use std::collections::HashSet;
+
+    use crate::{
+        Schema,
+        bigdecimal::{big_decimal_as_bytes, deserialize_big_decimal},
+        schema::{Name, Namespace, RecordField, UnionSchema},
+        serde::with::BytesType,
+    };
+
+    /// Returns `Schema::Union(Schema::Null, Schema::BigDecimal)`
+    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: &Namespace) -> 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(
+        _: usize,
+        _: &mut HashSet<Name>,
+        _: &Namespace,
+    ) -> Option<Vec<RecordField>> {
+        None
+    }
+
+    pub fn serialize<S, B>(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(|e| 
S::Error::custom(e))?;
+            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<&'de [u8]> = serde_bytes::deserialize(deserializer)?;
+        if let Some(bytes) = bytes {
+            deserialize_big_decimal(bytes)
+                .map(Some)
+                .map_err(|e| D::Error::custom(e))
+        } 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)]
+///     #[serde(with = "apache_avro::serde::array")]
+///     array: [i32; 10],
+/// }
+/// ```
+///
+/// [`apache_avro::serde::array_opt`]: array_opt
+pub mod array {
+    use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as 
_};
+    use std::collections::HashSet;
+
+    use crate::{
+        AvroSchema, AvroSchemaComponent, Schema,
+        schema::{Name, Namespace, RecordField},
+    };
+
+    /// Returns `Schema::Array(T::get_schema())`
+    pub fn get_schema_in_ctxt<T: AvroSchemaComponent>(
+        _: &mut HashSet<Name>,
+        _: &Namespace,
+    ) -> Schema {
+        Schema::array(T::get_schema()).build()
+    }
+
+    /// Returns `None`
+    pub fn get_record_fields_in_ctxt(
+        _: usize,
+        _: &mut HashSet<Name>,
+        _: &Namespace,
+    ) -> 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: Deserialize<'de>,
+    {
+        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)]
+///     #[serde(with = "apache_avro::serde::array_opt")]
+///     array: Option<[i32; 10]>,
+/// }
+/// ```
+///
+/// [`apache_avro::serde::array`]: array
+pub mod array_opt {
+    use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as 
_};
+    use std::collections::HashSet;
+
+    use crate::{
+        AvroSchema, AvroSchemaComponent, Schema,
+        schema::{Name, Namespace, RecordField, UnionSchema},
+    };
+
+    /// Returns `Schema::Union(Schema::Null, Schema::Array(T::get_schema()))`
+    pub fn get_schema_in_ctxt<T: AvroSchemaComponent>(
+        _: &mut HashSet<Name>,
+        _: &Namespace,
+    ) -> Schema {
+        Schema::Union(
+            UnionSchema::new(vec![Schema::Null, 
Schema::array(T::get_schema()).build()])
+                .expect("This is a valid union"),
+        )
+    }
+
+    /// Returns `None`
+    pub fn get_record_fields_in_ctxt(
+        _: usize,
+        _: &mut HashSet<Name>,
+        _: &Namespace,
+    ) -> 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