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

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

commit 6eaa2cc71f1c14d7da559119231c841014744fbc
Author: default <[email protected]>
AuthorDate: Fri Jan 23 14:33:33 2026 +0000

    fix: Move the `bytes` module to the `serde` module
---
 avro/src/lib.rs                                 |  26 +-
 avro/src/serde/de.rs                            |   2 +-
 avro/src/serde/mod.rs                           |  11 +-
 avro/src/serde/ser.rs                           |   2 +-
 avro/src/serde/ser_schema.rs                    |   4 +-
 avro/src/serde/with.rs                          | 793 ++++++++++++++++++++++++
 avro/tests/avro-rs-285-bytes_deserialization.rs |   2 +-
 avro_derive/tests/serde.rs                      |  21 +-
 8 files changed, 822 insertions(+), 39 deletions(-)

diff --git a/avro/src/lib.rs b/avro/src/lib.rs
index 40be7f9..f370ff6 100644
--- a/avro/src/lib.rs
+++ b/avro/src/lib.rs
@@ -868,13 +868,13 @@
 //!
 //!  #[derive(Debug, Deserialize, Serialize)]
 //!  struct SampleStruct {
-//!   #[serde(with = "apache_avro::serde_avro_bytes")]
+//!   #[serde(with = "apache_avro::serde::bytes")]
 //!   non_optional_bytes: Vec<u8>,
-//!   #[serde(with = "apache_avro::serde_avro_bytes_opt")]
+//!   #[serde(with = "apache_avro::serde::bytes_opt")]
 //!   optional_bytes: Option<Vec<u8>>,
-//!   #[serde(with = "apache_avro::serde_avro_fixed")]
+//!   #[serde(with = "apache_avro::serde::fixed")]
 //!   non_optional_fixed: [u8; 6],
-//!   #[serde(with = "apache_avro::serde_avro_fixed_opt")]
+//!   #[serde(with = "apache_avro::serde::fixed_opt")]
 //!   optional_fixed: Option<[u8; 6]>,
 //!  }
 //! ```
@@ -886,7 +886,7 @@
 //!
 //! #[derive(Debug, Deserialize, PartialEq, Serialize)]
 //! struct ExampleByteArray {
-//!     #[serde(with = "apache_avro::serde_avro_bytes_opt")]
+//!     #[serde(with = "apache_avro::serde::bytes_opt")]
 //!     data_bytes: Option<Vec<u8>>,
 //!     description: Option<String>,
 //! }
@@ -938,19 +938,14 @@
 //!   assert_eq!(records, deserialized_records);
 //! }
 //! ```
-//!
-//! Full implementation and other options for things like fixed byte arrays 
can be found in src/bytes.rs
-//!
 
 mod bigdecimal;
-mod bytes;
 mod codec;
 mod decimal;
 mod decode;
 mod duration;
 mod encode;
 mod reader;
-mod serde;
 mod writer;
 
 pub mod error;
@@ -959,17 +954,12 @@ pub mod rabin;
 pub mod schema;
 pub mod schema_compatibility;
 pub mod schema_equality;
+pub mod serde;
 pub mod types;
 pub mod util;
 pub mod validator;
 
-pub use crate::{
-    bigdecimal::BigDecimal,
-    bytes::{
-        serde_avro_bytes, serde_avro_bytes_opt, serde_avro_fixed, 
serde_avro_fixed_opt,
-        serde_avro_slice, serde_avro_slice_opt,
-    },
-};
+pub use crate::bigdecimal::BigDecimal;
 #[cfg(feature = "bzip")]
 pub use codec::bzip::Bzip2Settings;
 #[cfg(feature = "xz")]
@@ -985,7 +975,7 @@ pub use reader::{
     from_avro_datum_reader_schemata, from_avro_datum_schemata, read_marker,
 };
 pub use schema::{AvroSchema, AvroSchemaComponent, Schema};
-pub use serde::{de::from_value, ser::to_value};
+pub use serde::{from_value, to_value};
 pub use uuid::Uuid;
 pub use writer::{
     GenericSingleObjectWriter, SpecificSingleObjectWriter, Writer, 
WriterBuilder, to_avro_datum,
diff --git a/avro/src/serde/de.rs b/avro/src/serde/de.rs
index 6779502..18c42a2 100644
--- a/avro/src/serde/de.rs
+++ b/avro/src/serde/de.rs
@@ -16,7 +16,7 @@
 // under the License.
 
 //! Logic for serde-compatible deserialization.
-use crate::{Error, bytes::DE_BYTES_BORROWED, error::Details, types::Value};
+use crate::{Error, error::Details, serde::with::DE_BYTES_BORROWED, 
types::Value};
 use serde::{
     Deserialize,
     de::{self, DeserializeSeed, Deserializer as _, Visitor},
diff --git a/avro/src/serde/mod.rs b/avro/src/serde/mod.rs
index efd1032..01852a8 100644
--- a/avro/src/serde/mod.rs
+++ b/avro/src/serde/mod.rs
@@ -15,7 +15,12 @@
 // specific language governing permissions and limitations
 // under the License.
 
-pub mod de;
-pub mod ser;
-pub mod ser_schema;
+mod de;
+mod ser;
+pub(crate) mod ser_schema;
 mod util;
+mod with;
+
+pub use de::from_value;
+pub use ser::to_value;
+pub use with::{bytes, bytes_opt, fixed, fixed_opt, slice, slice_opt};
diff --git a/avro/src/serde/ser.rs b/avro/src/serde/ser.rs
index 475d8d5..76c6579 100644
--- a/avro/src/serde/ser.rs
+++ b/avro/src/serde/ser.rs
@@ -18,7 +18,7 @@
 //! Logic for serde-compatible serialization.
 use crate::{
     Error,
-    bytes::{BytesType, SER_BYTES_TYPE},
+    serde::with::{BytesType, SER_BYTES_TYPE},
     types::Value,
 };
 use serde::{Serialize, ser};
diff --git a/avro/src/serde/ser_schema.rs b/avro/src/serde/ser_schema.rs
index 79a58a6..3ca05e1 100644
--- a/avro/src/serde/ser_schema.rs
+++ b/avro/src/serde/ser_schema.rs
@@ -3581,10 +3581,10 @@ mod tests {
         let mut buffer: Vec<u8> = Vec::new();
         let names = HashMap::new();
         let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
-        let bytes_written = crate::serde_avro_fixed::serialize(&[0, 1, 2, 3], 
&mut serializer)?;
+        let bytes_written = crate::serde::fixed::serialize(&[0, 1, 2, 3], &mut 
serializer)?;
         assert_eq!(bytes_written, 4);
         let bytes_written =
-            crate::serde_avro_fixed::serialize(&[4, 5, 6, 7, 8, 9, 10, 11], 
&mut serializer)?;
+            crate::serde::fixed::serialize(&[4, 5, 6, 7, 8, 9, 10, 11], &mut 
serializer)?;
         assert_eq!(bytes_written, 8);
 
         assert_eq!(buffer, &[0, 0, 1, 2, 3, 2, 4, 5, 6, 7, 8, 9, 10, 11][..]);
diff --git a/avro/src/serde/with.rs b/avro/src/serde/with.rs
new file mode 100644
index 0000000..24530dd
--- /dev/null
+++ b/avro/src/serde/with.rs
@@ -0,0 +1,793 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::cell::Cell;
+
+thread_local! {
+    /// A thread local that is used to decide how to serialize Rust bytes into 
an Avro
+    /// `types::Value` of type bytes.
+    ///
+    /// 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) };
+
+    /// A thread local that is used to decide how to deserialize an Avro 
`types::Value`
+    /// of type bytes into Rust bytes.
+    ///
+    /// Relies on the fact that serde's deserialization process is 
single-threaded.
+    pub(crate) static DE_BYTES_BORROWED: Cell<bool> = const { Cell::new(false) 
};
+}
+
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum BytesType {
+    Bytes,
+    Fixed,
+}
+
+/// Efficient (de)serialization of Avro bytes values.
+///
+/// This module is intended to be used through the Serde `with` attribute.
+/// Use [`apache_avro::serde::bytes_opt`] for optional bytes.
+///
+/// See usage with below example:
+/// ```
+/// # use apache_avro::AvroSchema;
+/// # use serde::{Deserialize, Serialize};
+///
+/// #[derive(AvroSchema, Serialize, Deserialize)]
+/// struct StructWithBytes {
+///     #[avro(with)]
+///     #[serde(with = "apache_avro::serde::bytes")]
+///     vec_field: Vec<u8>,
+///
+///     #[avro(with = apache_avro::serde::fixed::get_schema_in_ctxt::<6>)]
+///     #[serde(with = "apache_avro::serde::fixed")]
+///     fixed_field: [u8; 6],
+/// }
+/// ```
+///
+/// [`apache_avro::serde::bytes_opt`]: bytes_opt
+pub mod bytes {
+    use serde::{Deserializer, Serializer};
+
+    use crate::{
+        Schema,
+        schema::{Names, Namespace},
+    };
+
+    /// Returns [`Schema::Bytes`]
+    pub fn get_schema_in_ctxt(_names: &mut Names, _enclosing_namespace: 
&Namespace) -> Schema {
+        Schema::Bytes
+    }
+
+    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serde_bytes::serialize(bytes, serializer)
+    }
+
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        serde_bytes::deserialize(deserializer)
+    }
+}
+
+/// Efficient (de)serialization of optional Avro bytes values.
+///
+/// This module is intended to be used through the Serde `with` attribute.
+/// Use [`apache_avro::serde::bytes`] for non-optional bytes.
+///
+/// See usage with below example:
+/// ```
+/// # use apache_avro::AvroSchema;
+/// # use serde::{Deserialize, Serialize};
+///
+/// #[derive(AvroSchema, Serialize, Deserialize)]
+/// struct StructWithBytes {
+///     #[avro(with)]
+///     #[serde(with = "apache_avro::serde::bytes_opt")]
+///     vec_field: Option<Vec<u8>>,
+///
+///     #[avro(with = apache_avro::serde::fixed_opt::get_schema_in_ctxt::<6>)]
+///     #[serde(with = "apache_avro::serde::fixed_opt")]
+///     fixed_field: Option<[u8; 6]>,
+/// }
+/// ```
+///
+/// [`apache_avro::serde::bytes`]: bytes
+pub mod bytes_opt {
+    use serde::{Deserializer, Serializer};
+    use std::borrow::Borrow;
+
+    use crate::{
+        Schema,
+        schema::{Names, Namespace, UnionSchema},
+    };
+
+    /// Returns `Schema::Union(Schema::Null, Schema::Bytes)`
+    pub fn get_schema_in_ctxt(_names: &mut Names, _enclosing_namespace: 
&Namespace) -> Schema {
+        Schema::Union(
+            UnionSchema::new(vec![Schema::Null, Schema::Bytes]).expect("This 
is a valid union"),
+        )
+    }
+
+    pub fn serialize<S, B>(bytes: &Option<B>, serializer: S) -> Result<S::Ok, 
S::Error>
+    where
+        S: Serializer,
+        B: Borrow<[u8]> + serde_bytes::Serialize,
+    {
+        serde_bytes::serialize(bytes, serializer)
+    }
+
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, 
D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        serde_bytes::deserialize(deserializer)
+    }
+}
+
+/// Efficient (de)serialization of Avro fixed values.
+///
+/// This module is intended to be used through the Serde `with` attribute.
+/// Use [`apache_avro::serde::fixed_opt`] for optional fixed values.
+///
+/// See usage with below example:
+/// ```
+/// # use apache_avro::AvroSchema;
+/// # use serde::{Deserialize, Serialize};
+///
+/// #[derive(AvroSchema, Serialize, Deserialize)]
+/// struct StructWithBytes {
+///     #[avro(with)]
+///     #[serde(with = "apache_avro::serde::bytes")]
+///     vec_field: Vec<u8>,
+///
+///     #[avro(with = apache_avro::serde::fixed::get_schema_in_ctxt::<6>)]
+///     #[serde(with = "apache_avro::serde::fixed")]
+///     fixed_field: [u8; 6],
+/// }
+/// ```
+///
+/// [`apache_avro::serde::fixed_opt`]: fixed_opt
+pub mod fixed {
+    use super::{BytesType, SER_BYTES_TYPE};
+    use serde::{Deserializer, Serializer};
+
+    use crate::{
+        Schema,
+        schema::{FixedSchema, Name, Names, Namespace},
+    };
+
+    /// Returns `Schema::Fixed(N)` named `serde_avro_fixed_{N}`
+    #[expect(clippy::map_entry, reason = "We don't use the value from the 
map")]
+    pub fn get_schema_in_ctxt<const N: usize>(
+        named_schemas: &mut Names,
+        enclosing_namespace: &Namespace,
+    ) -> Schema {
+        let name = Name::new(&format!("serde_avro_fixed_{N}"))
+            .expect("Name is valid")
+            .fully_qualified_name(enclosing_namespace);
+        if named_schemas.contains_key(&name) {
+            Schema::Ref { name }
+        } else {
+            let schema = 
Schema::Fixed(FixedSchema::builder().name(name.clone()).size(N).build());
+            named_schemas.insert(name, schema.clone());
+            schema
+        }
+    }
+
+    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        SER_BYTES_TYPE.set(BytesType::Fixed);
+        let res = serde_bytes::serialize(bytes, serializer);
+        SER_BYTES_TYPE.set(BytesType::Bytes);
+        res
+    }
+
+    pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; 
N], D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        serde_bytes::deserialize(deserializer)
+    }
+}
+
+/// Efficient (de)serialization of optional Avro fixed values.
+///
+/// This module is intended to be used through the Serde `with` attribute.
+/// Use [`apache_avro::serde::fixed`] for non-optional fixed values.
+///
+/// See usage with below example:
+/// ```
+/// # use apache_avro::AvroSchema;
+/// # use serde::{Deserialize, Serialize};
+///
+/// #[derive(AvroSchema, Serialize, Deserialize)]
+/// struct StructWithBytes {
+///     #[avro(with)]
+///     #[serde(with = "apache_avro::serde::bytes_opt")]
+///     vec_field: Option<Vec<u8>>,
+///
+///     #[avro(with = apache_avro::serde::fixed_opt::get_schema_in_ctxt::<6>)]
+///     #[serde(with = "apache_avro::serde::fixed_opt")]
+///     fixed_field: Option<[u8; 6]>,
+/// }
+/// ```
+///
+/// [`apache_avro::serde::fixed`]: fixed
+pub mod fixed_opt {
+    use super::{BytesType, SER_BYTES_TYPE};
+    use serde::{Deserializer, Serializer};
+    use std::borrow::Borrow;
+
+    use crate::{
+        Schema,
+        schema::{Names, Namespace, UnionSchema},
+    };
+
+    /// Returns `Schema::Union(Schema::Null, Schema::Fixed(N))` where the 
fixed schema is named `serde_avro_fixed_{N}`
+    pub fn get_schema_in_ctxt<const N: usize>(
+        named_schemas: &mut Names,
+        enclosing_namespace: &Namespace,
+    ) -> Schema {
+        Schema::Union(
+            UnionSchema::new(vec![
+                Schema::Null,
+                super::fixed::get_schema_in_ctxt::<N>(named_schemas, 
enclosing_namespace),
+            ])
+            .expect("This is a valid union"),
+        )
+    }
+
+    pub fn serialize<S, B>(bytes: &Option<B>, serializer: S) -> Result<S::Ok, 
S::Error>
+    where
+        S: Serializer,
+        B: Borrow<[u8]> + serde_bytes::Serialize,
+    {
+        SER_BYTES_TYPE.set(BytesType::Fixed);
+        let res = serde_bytes::serialize(bytes, serializer);
+        SER_BYTES_TYPE.set(BytesType::Bytes);
+        res
+    }
+
+    pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> 
Result<Option<[u8; N]>, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        serde_bytes::deserialize(deserializer)
+    }
+}
+
+/// Efficient (de)serialization of Avro bytes/fixed borrowed values.
+///
+/// This module is intended to be used through the Serde `with` attribute.
+///
+/// Note that `&[u8]` are always serialized as [`Value::Bytes`]. However,
+/// both [`Value::Bytes`] and [`Value::Fixed`] can be deserialized as `&[u8]`.
+///
+/// Use [`apache_avro::serde::slice`] for optional bytes/fixed borrowed values.
+///
+/// See usage with below example:
+/// ```rust
+/// # use apache_avro::AvroSchema;
+/// # use serde::{Deserialize, Serialize};
+///
+/// #[derive(AvroSchema, Serialize, Deserialize)]
+/// struct StructWithBytes<'a> {
+///     #[avro(with)]
+///     #[serde(with = "apache_avro::serde::slice")]
+///     slice_field: &'a [u8],
+/// }
+/// ```
+///
+/// [`Value::Bytes`]: crate::types::Value::Bytes
+/// [`Value::Fixed`]: crate::types::Value::Fixed
+/// [`apache_avro::serde::slice`]: slice_opt
+pub mod slice {
+    use super::DE_BYTES_BORROWED;
+    use serde::{Deserializer, Serializer};
+
+    use crate::{
+        Schema,
+        schema::{Names, Namespace},
+    };
+
+    /// Returns [`Schema::Bytes`]
+    pub fn get_schema_in_ctxt(_names: &mut Names, _enclosing_namespace: 
&Namespace) -> Schema {
+        Schema::Bytes
+    }
+
+    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serde_bytes::serialize(bytes, serializer)
+    }
+
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<&'de [u8], D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        DE_BYTES_BORROWED.set(true);
+        let res = serde_bytes::deserialize(deserializer);
+        DE_BYTES_BORROWED.set(false);
+        res
+    }
+}
+
+/// Efficient (de)serialization of optional Avro bytes/fixed borrowed values.
+///
+/// This module is intended to be used through the Serde `with` attribute.
+///
+/// Note that `&[u8]` are always serialized as [`Value::Bytes`]. However,
+/// both [`Value::Bytes`] and [`Value::Fixed`] can be deserialized as `&[u8]`.
+///
+/// Use [`apache_avro::serde::slice`] for non-optional bytes/fixed borrowed 
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::slice_opt")]
+///     slice_field: Option<&'a [u8]>,
+/// }
+/// ```
+///
+/// [`Value::Bytes`]: crate::types::Value::Bytes
+/// [`Value::Fixed`]: crate::types::Value::Fixed
+/// [`apache_avro::serde::slice`]: mod@slice
+pub mod slice_opt {
+    use super::DE_BYTES_BORROWED;
+    use serde::{Deserializer, Serializer};
+    use std::borrow::Borrow;
+
+    use crate::{
+        Schema,
+        schema::{Names, Namespace, UnionSchema},
+    };
+
+    /// Returns `Schema::Union(Schema::Null, Schema::Bytes)`
+    pub fn get_schema_in_ctxt(_names: &mut Names, _enclosing_namespace: 
&Namespace) -> Schema {
+        Schema::Union(
+            UnionSchema::new(vec![Schema::Null, Schema::Bytes]).expect("This 
is a valid union"),
+        )
+    }
+
+    pub fn serialize<S, B>(bytes: &Option<B>, serializer: S) -> Result<S::Ok, 
S::Error>
+    where
+        S: Serializer,
+        B: Borrow<[u8]> + serde_bytes::Serialize,
+    {
+        serde_bytes::serialize(&bytes, serializer)
+    }
+
+    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<&'de [u8]>, 
D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        DE_BYTES_BORROWED.set(true);
+        let res = serde_bytes::deserialize(deserializer);
+        DE_BYTES_BORROWED.set(false);
+        res
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{Schema, from_value, to_value, types::Value};
+    use serde::{Deserialize, Serialize};
+
+    #[test]
+    fn avro_3631_validate_schema_for_struct_with_byte_types() {
+        #[derive(Debug, Serialize)]
+        struct TestStructWithBytes<'a> {
+            #[serde(with = "crate::serde::bytes")]
+            vec_field: Vec<u8>,
+            #[serde(with = "crate::serde::bytes_opt")]
+            vec_field_opt: Option<Vec<u8>>,
+
+            #[serde(with = "crate::serde::fixed")]
+            fixed_field: [u8; 6],
+            #[serde(with = "crate::serde::fixed_opt")]
+            fixed_field_opt: Option<[u8; 7]>,
+
+            #[serde(with = "crate::serde::slice")]
+            slice_field: &'a [u8],
+            #[serde(with = "crate::serde::slice_opt")]
+            slice_field_opt: Option<&'a [u8]>,
+        }
+
+        let test = TestStructWithBytes {
+            vec_field: vec![2, 3, 4],
+            vec_field_opt: Some(vec![2, 3, 4]),
+            fixed_field: [1; 6],
+            fixed_field_opt: Some([1; 7]),
+            slice_field: &[1, 2, 3],
+            slice_field_opt: Some(&[1, 2, 3]),
+        };
+        let value: Value = to_value(test).unwrap();
+        let schema = Schema::parse_str(
+            r#"
+            {
+              "type": "record",
+              "name": "TestStructWithBytes",
+              "fields": [ {
+                "name": "vec_field",
+                "type": "bytes"
+              }, {
+                "name": "vec_field_opt",
+                "type": ["null", "bytes"]
+              }, {
+                "name": "fixed_field",
+                "type": {
+                  "name": "ByteData",
+                  "type": "fixed",
+                  "size": 6
+                }
+              }, {
+                "name": "fixed_field_opt",
+                "type": ["null", {
+                  "name": "ByteData2",
+                  "type": "fixed",
+                  "size": 7
+                } ]
+              }, {
+                "name": "slice_field",
+                "type": "bytes"
+              }, {
+                "name": "slice_field_opt",
+                "type": ["null", "bytes"]
+              } ]
+            }"#,
+        )
+        .unwrap();
+        assert!(value.validate(&schema));
+    }
+
+    #[test]
+    fn avro_3631_deserialize_value_to_struct_with_byte_types() {
+        #[derive(Debug, Deserialize, PartialEq)]
+        struct TestStructWithBytes<'a> {
+            #[serde(with = "crate::serde::bytes")]
+            vec_field: Vec<u8>,
+            #[serde(with = "crate::serde::bytes_opt")]
+            vec_field_opt: Option<Vec<u8>>,
+            #[serde(with = "crate::serde::bytes_opt")]
+            vec_field_opt2: Option<Vec<u8>>,
+
+            #[serde(with = "crate::serde::fixed")]
+            fixed_field: [u8; 6],
+            #[serde(with = "crate::serde::fixed_opt")]
+            fixed_field_opt: Option<[u8; 7]>,
+            #[serde(with = "crate::serde::fixed_opt")]
+            fixed_field_opt2: Option<[u8; 8]>,
+
+            #[serde(with = "crate::serde::slice")]
+            slice_bytes_field: &'a [u8],
+            #[serde(with = "crate::serde::slice_opt")]
+            slice_bytes_field_opt: Option<&'a [u8]>,
+            #[serde(with = "crate::serde::slice_opt")]
+            slice_bytes_field_opt2: Option<&'a [u8]>,
+
+            #[serde(with = "crate::serde::slice")]
+            slice_fixed_field: &'a [u8],
+            #[serde(with = "crate::serde::slice_opt")]
+            slice_fixed_field_opt: Option<&'a [u8]>,
+            #[serde(with = "crate::serde::slice_opt")]
+            slice_fixed_field_opt2: Option<&'a [u8]>,
+        }
+
+        let expected = TestStructWithBytes {
+            vec_field: vec![3, 33],
+            vec_field_opt: Some(vec![4, 44]),
+            vec_field_opt2: None,
+            fixed_field: [1; 6],
+            fixed_field_opt: Some([7; 7]),
+            fixed_field_opt2: None,
+            slice_bytes_field: &[1, 11, 111],
+            slice_bytes_field_opt: Some(&[5, 5, 5, 5, 5]),
+            slice_bytes_field_opt2: None,
+            slice_fixed_field: &[2, 22, 222],
+            slice_fixed_field_opt: Some(&[3, 3, 3]),
+            slice_fixed_field_opt2: None,
+        };
+
+        let value = Value::Record(vec![
+            (
+                "vec_field".to_owned(),
+                Value::Bytes(expected.vec_field.clone()),
+            ),
+            (
+                "vec_field_opt".to_owned(),
+                Value::Union(
+                    1,
+                    Box::new(Value::Bytes(
+                        expected.vec_field_opt.as_ref().unwrap().clone(),
+                    )),
+                ),
+            ),
+            (
+                "vec_field_opt2".to_owned(),
+                Value::Union(0, Box::new(Value::Null)),
+            ),
+            (
+                "fixed_field".to_owned(),
+                Value::Fixed(expected.fixed_field.len(), 
expected.fixed_field.to_vec()),
+            ),
+            (
+                "fixed_field_opt".to_owned(),
+                Value::Union(
+                    1,
+                    Box::new(Value::Fixed(
+                        expected.fixed_field_opt.as_ref().unwrap().len(),
+                        expected.fixed_field_opt.as_ref().unwrap().to_vec(),
+                    )),
+                ),
+            ),
+            (
+                "fixed_field_opt2".to_owned(),
+                Value::Union(0, Box::new(Value::Null)),
+            ),
+            (
+                "slice_bytes_field".to_owned(),
+                Value::Bytes(expected.slice_bytes_field.to_vec()),
+            ),
+            (
+                "slice_bytes_field_opt".to_owned(),
+                Value::Union(
+                    1,
+                    Box::new(Value::Bytes(
+                        
expected.slice_bytes_field_opt.as_ref().unwrap().to_vec(),
+                    )),
+                ),
+            ),
+            (
+                "slice_bytes_field_opt2".to_owned(),
+                Value::Union(0, Box::new(Value::Null)),
+            ),
+            (
+                "slice_fixed_field".to_owned(),
+                Value::Fixed(
+                    expected.slice_fixed_field.len(),
+                    expected.slice_fixed_field.to_vec(),
+                ),
+            ),
+            (
+                "slice_fixed_field_opt".to_owned(),
+                Value::Union(
+                    1,
+                    Box::new(Value::Fixed(
+                        expected.slice_fixed_field_opt.as_ref().unwrap().len(),
+                        
expected.slice_fixed_field_opt.as_ref().unwrap().to_vec(),
+                    )),
+                ),
+            ),
+            (
+                "slice_fixed_field_opt2".to_owned(),
+                Value::Union(1, Box::new(Value::Null)),
+            ),
+        ]);
+        assert_eq!(expected, from_value(&value).unwrap());
+    }
+
+    #[test]
+    fn avro_3631_serialize_struct_to_value_with_byte_types() {
+        #[derive(Debug, Serialize)]
+        struct TestStructWithBytes<'a> {
+            array_field: &'a [u8],
+            vec_field: Vec<u8>,
+
+            #[serde(with = "crate::serde::fixed")]
+            vec_field2: Vec<u8>,
+            #[serde(with = "crate::serde::fixed_opt")]
+            vec_field2_opt: Option<Vec<u8>>,
+            #[serde(with = "crate::serde::fixed_opt")]
+            vec_field2_opt2: Option<Vec<u8>>,
+
+            #[serde(with = "crate::serde::bytes")]
+            vec_field3: Vec<u8>,
+            #[serde(with = "crate::serde::bytes_opt")]
+            vec_field3_opt: Option<Vec<u8>>,
+            #[serde(with = "crate::serde::bytes_opt")]
+            vec_field3_opt2: Option<Vec<u8>>,
+
+            #[serde(with = "crate::serde::fixed")]
+            fixed_field: [u8; 6],
+            #[serde(with = "crate::serde::fixed_opt")]
+            fixed_field_opt: Option<[u8; 5]>,
+            #[serde(with = "crate::serde::fixed_opt")]
+            fixed_field_opt2: Option<[u8; 4]>,
+
+            #[serde(with = "crate::serde::fixed")]
+            fixed_field2: &'a [u8],
+            #[serde(with = "crate::serde::fixed_opt")]
+            fixed_field2_opt: Option<&'a [u8]>,
+            #[serde(with = "crate::serde::fixed_opt")]
+            fixed_field2_opt2: Option<&'a [u8]>,
+
+            #[serde(with = "crate::serde::bytes")]
+            bytes_field: &'a [u8],
+            #[serde(with = "crate::serde::bytes_opt")]
+            bytes_field_opt: Option<&'a [u8]>,
+            #[serde(with = "crate::serde::bytes_opt")]
+            bytes_field_opt2: Option<&'a [u8]>,
+
+            #[serde(with = "crate::serde::bytes")]
+            bytes_field2: [u8; 6],
+            #[serde(with = "crate::serde::bytes_opt")]
+            bytes_field2_opt: Option<[u8; 7]>,
+            #[serde(with = "crate::serde::bytes_opt")]
+            bytes_field2_opt2: Option<[u8; 8]>,
+        }
+
+        let test = TestStructWithBytes {
+            array_field: &[1, 11, 111],
+            vec_field: vec![3, 33],
+            vec_field2: vec![4, 44],
+            vec_field2_opt: Some(vec![14, 144]),
+            vec_field2_opt2: None,
+            vec_field3: vec![5, 55],
+            vec_field3_opt: Some(vec![15, 155]),
+            vec_field3_opt2: None,
+            fixed_field: [1; 6],
+            fixed_field_opt: Some([6; 5]),
+            fixed_field_opt2: None,
+            fixed_field2: &[6, 66],
+            fixed_field2_opt: Some(&[7, 77]),
+            fixed_field2_opt2: None,
+            bytes_field: &[2, 22, 222],
+            bytes_field_opt: Some(&[3, 33, 233]),
+            bytes_field_opt2: None,
+            bytes_field2: [2; 6],
+            bytes_field2_opt: Some([2; 7]),
+            bytes_field2_opt2: None,
+        };
+        let expected = Value::Record(vec![
+            (
+                "array_field".to_owned(),
+                Value::Array(
+                    test.array_field
+                        .iter()
+                        .map(|&i| Value::Int(i as i32))
+                        .collect(),
+                ),
+            ),
+            (
+                "vec_field".to_owned(),
+                Value::Array(
+                    test.vec_field
+                        .iter()
+                        .map(|&i| Value::Int(i as i32))
+                        .collect(),
+                ),
+            ),
+            (
+                "vec_field2".to_owned(),
+                Value::Fixed(test.vec_field2.len(), test.vec_field2.clone()),
+            ),
+            (
+                "vec_field2_opt".to_owned(),
+                Value::Union(
+                    1,
+                    Box::new(Value::Fixed(
+                        test.vec_field2_opt.as_ref().unwrap().len(),
+                        test.vec_field2_opt.as_ref().unwrap().to_vec(),
+                    )),
+                ),
+            ),
+            (
+                "vec_field2_opt2".to_owned(),
+                Value::Union(0, Box::new(Value::Null)),
+            ),
+            (
+                "vec_field3".to_owned(),
+                Value::Bytes(test.vec_field3.clone()),
+            ),
+            (
+                "vec_field3_opt".to_owned(),
+                Value::Union(
+                    1,
+                    
Box::new(Value::Bytes(test.vec_field3_opt.as_ref().unwrap().clone())),
+                ),
+            ),
+            (
+                "vec_field3_opt2".to_owned(),
+                Value::Union(0, Box::new(Value::Null)),
+            ),
+            (
+                "fixed_field".to_owned(),
+                Value::Fixed(test.fixed_field.len(), 
test.fixed_field.to_vec()),
+            ),
+            (
+                "fixed_field_opt".to_owned(),
+                Value::Union(
+                    1,
+                    Box::new(Value::Fixed(
+                        test.fixed_field_opt.as_ref().unwrap().len(),
+                        test.fixed_field_opt.as_ref().unwrap().to_vec(),
+                    )),
+                ),
+            ),
+            (
+                "fixed_field_opt2".to_owned(),
+                Value::Union(0, Box::new(Value::Null)),
+            ),
+            (
+                "fixed_field2".to_owned(),
+                Value::Fixed(test.fixed_field2.len(), 
test.fixed_field2.to_vec()),
+            ),
+            (
+                "fixed_field2_opt".to_owned(),
+                Value::Union(
+                    1,
+                    Box::new(Value::Fixed(
+                        test.fixed_field2_opt.as_ref().unwrap().len(),
+                        test.fixed_field2_opt.as_ref().unwrap().to_vec(),
+                    )),
+                ),
+            ),
+            (
+                "fixed_field2_opt2".to_owned(),
+                Value::Union(0, Box::new(Value::Null)),
+            ),
+            (
+                "bytes_field".to_owned(),
+                Value::Bytes(test.bytes_field.to_vec()),
+            ),
+            (
+                "bytes_field_opt".to_owned(),
+                Value::Union(
+                    1,
+                    Box::new(Value::Bytes(
+                        test.bytes_field_opt.as_ref().unwrap().to_vec(),
+                    )),
+                ),
+            ),
+            (
+                "bytes_field_opt2".to_owned(),
+                Value::Union(0, Box::new(Value::Null)),
+            ),
+            (
+                "bytes_field2".to_owned(),
+                Value::Bytes(test.bytes_field2.to_vec()),
+            ),
+            (
+                "bytes_field2_opt".to_owned(),
+                Value::Union(
+                    1,
+                    Box::new(Value::Bytes(
+                        test.bytes_field2_opt.as_ref().unwrap().to_vec(),
+                    )),
+                ),
+            ),
+            (
+                "bytes_field2_opt2".to_owned(),
+                Value::Union(0, Box::new(Value::Null)),
+            ),
+        ]);
+        assert_eq!(expected, to_value(test).unwrap());
+    }
+}
diff --git a/avro/tests/avro-rs-285-bytes_deserialization.rs 
b/avro/tests/avro-rs-285-bytes_deserialization.rs
index 5ac2688..b9ebe80 100644
--- a/avro/tests/avro-rs-285-bytes_deserialization.rs
+++ b/avro/tests/avro-rs-285-bytes_deserialization.rs
@@ -20,7 +20,7 @@ use serde::{Deserialize, Serialize};
 
 #[derive(Debug, Deserialize, PartialEq, Serialize)]
 struct ExampleByteArray {
-    #[serde(with = "apache_avro::serde_avro_bytes_opt")]
+    #[serde(with = "apache_avro::serde::bytes_opt")]
     data_bytes: Option<Vec<u8>>,
     description: Option<String>,
 }
diff --git a/avro_derive/tests/serde.rs b/avro_derive/tests/serde.rs
index 043a245..e14503d 100644
--- a/avro_derive/tests/serde.rs
+++ b/avro_derive/tests/serde.rs
@@ -570,33 +570,28 @@ mod field_attributes {
 
     #[test]
     fn avro_rs_397_avroschema_with_bytes() {
-        use apache_avro::{
-            serde_avro_bytes, serde_avro_bytes_opt, serde_avro_fixed, 
serde_avro_fixed_opt,
-            serde_avro_slice, serde_avro_slice_opt,
-        };
-
         #[expect(dead_code, reason = "We only care about the schema")]
         #[derive(AvroSchema)]
         struct TestStructWithBytes<'a> {
             #[avro(with)]
-            #[serde(with = "serde_avro_bytes")]
+            #[serde(with = "apache_avro::serde::bytes")]
             vec_field: Vec<u8>,
             #[avro(with)]
-            #[serde(with = "serde_avro_bytes_opt")]
+            #[serde(with = "apache_avro::serde::bytes_opt")]
             vec_field_opt: Option<Vec<u8>>,
 
-            #[avro(with = serde_avro_fixed::get_schema_in_ctxt::<6>)]
-            #[serde(with = "serde_avro_fixed")]
+            #[avro(with = apache_avro::serde::fixed::get_schema_in_ctxt::<6>)]
+            #[serde(with = "apache_avro::serde::fixed")]
             fixed_field: [u8; 6],
-            #[avro(with = serde_avro_fixed_opt::get_schema_in_ctxt::<7>)]
-            #[serde(with = "serde_avro_fixed_opt")]
+            #[avro(with = 
apache_avro::serde::fixed_opt::get_schema_in_ctxt::<7>)]
+            #[serde(with = "apache_avro::serde::fixed_opt")]
             fixed_field_opt: Option<[u8; 7]>,
 
             #[avro(with)]
-            #[serde(with = "serde_avro_slice")]
+            #[serde(with = "apache_avro::serde::slice")]
             slice_field: &'a [u8],
             #[avro(with)]
-            #[serde(with = "serde_avro_slice_opt")]
+            #[serde(with = "apache_avro::serde::slice_opt")]
             slice_field_opt: Option<&'a [u8]>,
         }
 


Reply via email to