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]>, }
