Skip to main content

apache_avro/serde/
with.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::cell::Cell;
19
20thread_local! {
21    /// A thread local that is used to decide if Rust bytes need to be serialized to
22    /// [`Value::Bytes`] or [`Value::Fixed`].
23    ///
24    /// Relies on the fact that serde's serialization process is single-threaded.
25    pub(crate) static SER_BYTES_TYPE: Cell<BytesType> = const { Cell::new(BytesType::Unset) };
26
27    /// A thread local that is used to decide if a [`Value::Bytes`] needs to be deserialized to
28    /// a [`Vec`] or slice.
29    ///
30    /// Relies on the fact that serde's deserialization process is single-threaded.
31    pub(crate) static DE_BYTES_BORROWED: Cell<bool> = const { Cell::new(false) };
32}
33
34#[derive(Debug, Clone, Copy)]
35pub(crate) enum BytesType {
36    Unset,
37    Bytes,
38    Fixed,
39}
40
41struct BytesTypeGuard(BytesType);
42impl BytesTypeGuard {
43    fn set(temp: BytesType) -> Self {
44        let prev = SER_BYTES_TYPE.get();
45        SER_BYTES_TYPE.set(temp);
46        Self(prev)
47    }
48}
49
50impl Drop for BytesTypeGuard {
51    fn drop(&mut self) {
52        SER_BYTES_TYPE.set(self.0);
53    }
54}
55
56struct BorrowedGuard(bool);
57impl BorrowedGuard {
58    fn set(temp: bool) -> Self {
59        let prev = DE_BYTES_BORROWED.get();
60        DE_BYTES_BORROWED.set(temp);
61        Self(prev)
62    }
63}
64
65impl Drop for BorrowedGuard {
66    fn drop(&mut self) {
67        DE_BYTES_BORROWED.set(self.0);
68    }
69}
70
71/// Efficient (de)serialization of Avro bytes values.
72///
73/// This module is intended to be used through the Serde `with` attribute.
74/// Use [`apache_avro::serde::bytes_opt`] for optional bytes.
75///
76/// When used with different serialization formats, this is equivalent to [`serde_bytes`].
77///
78/// See usage with below example:
79/// ```
80/// # use apache_avro::AvroSchema;
81/// # use serde::{Deserialize, Serialize};
82/// #[derive(AvroSchema, Serialize, Deserialize)]
83/// struct StructWithBytes {
84///     #[avro(with)]
85///     #[serde(with = "apache_avro::serde::bytes")]
86///     vec_field: Vec<u8>,
87///
88///     #[avro(with = apache_avro::serde::fixed::get_schema_in_ctxt::<6>)]
89///     #[serde(with = "apache_avro::serde::fixed")]
90///     fixed_field: [u8; 6],
91/// }
92/// ```
93///
94/// [`apache_avro::serde::bytes_opt`]: bytes_opt
95pub mod bytes {
96    use std::collections::HashSet;
97
98    use serde::{Deserializer, Serializer};
99
100    use super::BytesType;
101    use crate::{
102        Schema,
103        schema::{Name, NamespaceRef, RecordField},
104    };
105
106    /// Returns [`Schema::Bytes`]
107    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: NamespaceRef) -> Schema {
108        Schema::Bytes
109    }
110
111    /// Returns `None`
112    pub fn get_record_fields_in_ctxt(
113        _: usize,
114        _: &mut HashSet<Name>,
115        _: NamespaceRef,
116    ) -> Option<Vec<RecordField>> {
117        None
118    }
119
120    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
121    where
122        S: Serializer,
123    {
124        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
125        serde_bytes::serialize(bytes, serializer)
126    }
127
128    pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
129    where
130        D: Deserializer<'de>,
131    {
132        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
133        serde_bytes::deserialize(deserializer)
134    }
135}
136
137/// Efficient (de)serialization of optional Avro bytes values.
138///
139/// This module is intended to be used through the Serde `with` attribute.
140/// Use [`apache_avro::serde::bytes`] for non-optional bytes.
141///
142/// When used with different serialization formats, this is equivalent to [`serde_bytes`].
143///
144/// See usage with below example:
145/// ```
146/// # use apache_avro::AvroSchema;
147/// # use serde::{Deserialize, Serialize};
148/// #[derive(AvroSchema, Serialize, Deserialize)]
149/// struct StructWithBytes {
150///     #[avro(with)]
151///     #[serde(with = "apache_avro::serde::bytes_opt")]
152///     vec_field: Option<Vec<u8>>,
153///
154///     #[avro(with = apache_avro::serde::fixed_opt::get_schema_in_ctxt::<6>)]
155///     #[serde(with = "apache_avro::serde::fixed_opt")]
156///     fixed_field: Option<[u8; 6]>,
157/// }
158/// ```
159///
160/// [`apache_avro::serde::bytes`]: bytes
161pub mod bytes_opt {
162    use std::{borrow::Borrow, collections::HashSet};
163
164    use serde::{Deserializer, Serializer};
165
166    use super::BytesType;
167    use crate::{
168        Schema,
169        schema::{Name, NamespaceRef, RecordField, UnionSchema},
170    };
171
172    /// Returns `Schema::Union(Schema::Null, Schema::Bytes)`
173    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: NamespaceRef) -> Schema {
174        Schema::Union(
175            UnionSchema::new(vec![Schema::Null, Schema::Bytes])
176                .unwrap_or_else(|_| unreachable!("This is a valid union")),
177        )
178    }
179
180    /// Returns `None`
181    pub fn get_record_fields_in_ctxt(
182        _: usize,
183        _: &mut HashSet<Name>,
184        _: NamespaceRef,
185    ) -> Option<Vec<RecordField>> {
186        None
187    }
188
189    pub fn serialize<S, B>(bytes: &Option<B>, serializer: S) -> Result<S::Ok, S::Error>
190    where
191        S: Serializer,
192        B: Borrow<[u8]> + serde_bytes::Serialize,
193    {
194        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
195        serde_bytes::serialize(bytes, serializer)
196    }
197
198    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
199    where
200        D: Deserializer<'de>,
201    {
202        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
203        serde_bytes::deserialize(deserializer)
204    }
205}
206
207/// Efficient (de)serialization of Avro fixed values.
208///
209/// This module is intended to be used through the Serde `with` attribute.
210/// Use [`apache_avro::serde::fixed_opt`] for optional fixed values.
211///
212/// When used with different serialization formats, this is equivalent to [`serde_bytes`].
213///
214/// See usage with below example:
215/// ```
216/// # use apache_avro::AvroSchema;
217/// # use serde::{Deserialize, Serialize};
218/// #[derive(AvroSchema, Serialize, Deserialize)]
219/// struct StructWithBytes {
220///     #[avro(with)]
221///     #[serde(with = "apache_avro::serde::bytes")]
222///     vec_field: Vec<u8>,
223///
224///     #[avro(with = apache_avro::serde::fixed::get_schema_in_ctxt::<6>)]
225///     #[serde(with = "apache_avro::serde::fixed")]
226///     fixed_field: [u8; 6],
227/// }
228/// ```
229///
230/// [`apache_avro::serde::fixed_opt`]: fixed_opt
231pub mod fixed {
232    use std::collections::HashSet;
233
234    use serde::{Deserializer, Serializer};
235
236    use super::BytesType;
237    use crate::{
238        Schema,
239        schema::{FixedSchema, Name, NamespaceRef, RecordField},
240    };
241
242    /// Returns `Schema::Fixed(N)` named `serde_avro_fixed_{N}`
243    pub fn get_schema_in_ctxt<const N: usize>(
244        named_schemas: &mut HashSet<Name>,
245        enclosing_namespace: NamespaceRef,
246    ) -> Schema {
247        let name = Name::new_with_enclosing_namespace(
248            format!("serde_avro_fixed_{N}"),
249            enclosing_namespace,
250        )
251        .unwrap_or_else(|_| unreachable!("Name is valid"));
252        if named_schemas.contains(&name) {
253            Schema::Ref { name }
254        } else {
255            let schema = Schema::Fixed(FixedSchema::builder().name(name.clone()).size(N).build());
256            named_schemas.insert(name);
257            schema
258        }
259    }
260
261    /// Returns `None`
262    pub fn get_record_fields_in_ctxt(
263        _: usize,
264        _: &mut HashSet<Name>,
265        _: NamespaceRef,
266    ) -> Option<Vec<RecordField>> {
267        None
268    }
269
270    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
271    where
272        S: Serializer,
273    {
274        let _guard = super::BytesTypeGuard::set(BytesType::Fixed);
275        serde_bytes::serialize(bytes, serializer)
276    }
277
278    pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
279    where
280        D: Deserializer<'de>,
281    {
282        let _guard = super::BytesTypeGuard::set(BytesType::Fixed);
283        serde_bytes::deserialize(deserializer)
284    }
285}
286
287/// Efficient (de)serialization of optional Avro fixed values.
288///
289/// This module is intended to be used through the Serde `with` attribute.
290/// Use [`apache_avro::serde::fixed`] for non-optional fixed values.
291///
292/// When used with different serialization formats, this is equivalent to [`serde_bytes`].
293///
294/// See usage with below example:
295/// ```
296/// # use apache_avro::AvroSchema;
297/// # use serde::{Deserialize, Serialize};
298/// #[derive(AvroSchema, Serialize, Deserialize)]
299/// struct StructWithBytes {
300///     #[avro(with)]
301///     #[serde(with = "apache_avro::serde::bytes_opt")]
302///     vec_field: Option<Vec<u8>>,
303///
304///     #[avro(with = apache_avro::serde::fixed_opt::get_schema_in_ctxt::<6>)]
305///     #[serde(with = "apache_avro::serde::fixed_opt")]
306///     fixed_field: Option<[u8; 6]>,
307/// }
308/// ```
309///
310/// [`apache_avro::serde::fixed`]: fixed
311pub mod fixed_opt {
312    use std::{borrow::Borrow, collections::HashSet};
313
314    use serde::{Deserializer, Serializer};
315
316    use super::BytesType;
317    use crate::{
318        Schema,
319        schema::{Name, NamespaceRef, RecordField, UnionSchema},
320    };
321
322    /// Returns `Schema::Union(Schema::Null, Schema::Fixed(N))` where the fixed schema is named `serde_avro_fixed_{N}`
323    pub fn get_schema_in_ctxt<const N: usize>(
324        named_schemas: &mut HashSet<Name>,
325        enclosing_namespace: NamespaceRef,
326    ) -> Schema {
327        Schema::Union(
328            UnionSchema::new(vec![
329                Schema::Null,
330                super::fixed::get_schema_in_ctxt::<N>(named_schemas, enclosing_namespace),
331            ])
332            .unwrap_or_else(|_| unreachable!("This is a valid union")),
333        )
334    }
335
336    /// Returns `None`
337    pub fn get_record_fields_in_ctxt(
338        _: usize,
339        _: &mut HashSet<Name>,
340        _: NamespaceRef,
341    ) -> Option<Vec<RecordField>> {
342        None
343    }
344
345    pub fn serialize<S, B>(bytes: &Option<B>, serializer: S) -> Result<S::Ok, S::Error>
346    where
347        S: Serializer,
348        B: Borrow<[u8]> + serde_bytes::Serialize,
349    {
350        let _guard = super::BytesTypeGuard::set(BytesType::Fixed);
351        serde_bytes::serialize(bytes, serializer)
352    }
353
354    pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<Option<[u8; N]>, D::Error>
355    where
356        D: Deserializer<'de>,
357    {
358        let _guard = super::BytesTypeGuard::set(BytesType::Fixed);
359        serde_bytes::deserialize(deserializer)
360    }
361}
362
363/// Efficient (de)serialization of Avro bytes/fixed borrowed values.
364///
365/// This module is intended to be used through the Serde `with` attribute.
366///
367/// Note that `&[u8]` are always serialized as [`Value::Bytes`]. However,
368/// both [`Value::Bytes`] and [`Value::Fixed`] can be deserialized as `&[u8]`.
369///
370/// Use [`apache_avro::serde::slice_opt`] for optional bytes/fixed borrowed values.
371///
372/// When used with different serialization formats, this is equivalent to [`serde_bytes`].
373///
374/// See usage with below example:
375/// ```
376/// # use apache_avro::AvroSchema;
377/// # use serde::{Deserialize, Serialize};
378/// #[derive(AvroSchema, Serialize, Deserialize)]
379/// struct StructWithBytes<'a> {
380///     #[avro(with)]
381///     #[serde(with = "apache_avro::serde::slice")]
382///     slice_field: &'a [u8],
383/// }
384/// ```
385///
386/// [`Value::Bytes`]: crate::types::Value::Bytes
387/// [`Value::Fixed`]: crate::types::Value::Fixed
388/// [`apache_avro::serde::slice_opt`]: slice_opt
389pub mod slice {
390    use std::collections::HashSet;
391
392    use serde::{Deserializer, Serializer};
393
394    use super::BytesType;
395    use crate::{
396        Schema,
397        schema::{Name, NamespaceRef, RecordField},
398    };
399
400    /// Returns [`Schema::Bytes`]
401    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: NamespaceRef) -> Schema {
402        Schema::Bytes
403    }
404
405    /// Returns `None`
406    pub fn get_record_fields_in_ctxt(
407        _: usize,
408        _: &mut HashSet<Name>,
409        _: NamespaceRef,
410    ) -> Option<Vec<RecordField>> {
411        None
412    }
413
414    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
415    where
416        S: Serializer,
417    {
418        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
419        serde_bytes::serialize(bytes, serializer)
420    }
421
422    pub fn deserialize<'de, D>(deserializer: D) -> Result<&'de [u8], D::Error>
423    where
424        D: Deserializer<'de>,
425    {
426        let _bytes_guard = super::BytesTypeGuard::set(BytesType::Bytes);
427        let _guard = super::BorrowedGuard::set(true);
428        serde_bytes::deserialize(deserializer)
429    }
430}
431
432/// Efficient (de)serialization of optional Avro bytes/fixed borrowed values.
433///
434/// This module is intended to be used through the Serde `with` attribute.
435///
436/// Note that `&[u8]` are always serialized as [`Value::Bytes`]. However,
437/// both [`Value::Bytes`] and [`Value::Fixed`] can be deserialized as `&[u8]`.
438///
439/// Use [`apache_avro::serde::slice`] for non-optional bytes/fixed borrowed values.
440///
441/// When used with different serialization formats, this is equivalent to [`serde_bytes`].
442///
443/// See usage with below example:
444/// ```
445/// # use apache_avro::AvroSchema;
446/// # use serde::{Deserialize, Serialize};
447/// #[derive(AvroSchema, Serialize, Deserialize)]
448/// struct StructWithBytes<'a> {
449///     #[avro(with)]
450///     #[serde(with = "apache_avro::serde::slice_opt")]
451///     slice_field: Option<&'a [u8]>,
452/// }
453/// ```
454///
455/// [`Value::Bytes`]: crate::types::Value::Bytes
456/// [`Value::Fixed`]: crate::types::Value::Fixed
457/// [`apache_avro::serde::slice`]: mod@slice
458pub mod slice_opt {
459    use std::{borrow::Borrow, collections::HashSet};
460
461    use serde::{Deserializer, Serializer};
462
463    use super::BytesType;
464    use crate::{
465        Schema,
466        schema::{Name, NamespaceRef, RecordField, UnionSchema},
467    };
468
469    /// Returns `Schema::Union(Schema::Null, Schema::Bytes)`
470    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: NamespaceRef) -> Schema {
471        Schema::Union(
472            UnionSchema::new(vec![Schema::Null, Schema::Bytes])
473                .unwrap_or_else(|_| unreachable!("This is a valid union")),
474        )
475    }
476
477    /// Returns `None`
478    pub fn get_record_fields_in_ctxt(
479        _: usize,
480        _: &mut HashSet<Name>,
481        _: NamespaceRef,
482    ) -> Option<Vec<RecordField>> {
483        None
484    }
485
486    pub fn serialize<S, B>(bytes: &Option<B>, serializer: S) -> Result<S::Ok, S::Error>
487    where
488        S: Serializer,
489        B: Borrow<[u8]> + serde_bytes::Serialize,
490    {
491        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
492        serde_bytes::serialize(&bytes, serializer)
493    }
494
495    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<&'de [u8]>, D::Error>
496    where
497        D: Deserializer<'de>,
498    {
499        let _bytes_guard = super::BytesTypeGuard::set(BytesType::Bytes);
500        let _guard = super::BorrowedGuard::set(true);
501        serde_bytes::deserialize(deserializer)
502    }
503}
504
505/// (De)serialize [`BigDecimal`] as a [`Schema::BigDecimal`] instead of a [`Schema::String`].
506///
507/// This module is intended to be used through the Serde `with` attribute.
508///
509/// Use [`apache_avro::serde::bigdecimal_opt`] for optional big decimals values.
510///
511/// When used with different serialization formats, this will write bytes.
512///
513/// See usage with below example:
514/// ```
515/// # use apache_avro::{AvroSchema, BigDecimal};
516/// # use serde::{Deserialize, Serialize};
517/// #[derive(AvroSchema, Serialize, Deserialize)]
518/// struct StructWithBigDecimal {
519///     #[avro(with)]
520///     #[serde(with = "apache_avro::serde::bigdecimal")]
521///     decimal: BigDecimal,
522/// }
523/// ```
524///
525/// [`BigDecimal`]: ::bigdecimal::BigDecimal
526/// [`Schema::BigDecimal`]: crate::Schema::BigDecimal
527/// [`Schema::String`]: crate::Schema::String
528/// [`apache_avro::serde::bigdecimal_opt`]: bigdecimal_opt
529pub mod bigdecimal {
530    use std::collections::HashSet;
531
532    use bigdecimal::BigDecimal;
533    use serde::{Deserializer, Serializer, de::Error as _, ser::Error as _};
534
535    use crate::{
536        Schema,
537        bigdecimal::{big_decimal_as_bytes, deserialize_big_decimal},
538        schema::{Name, NamespaceRef, RecordField},
539        serde::with::BytesType,
540    };
541
542    /// Returns [`Schema::BigDecimal`]
543    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: NamespaceRef) -> Schema {
544        Schema::BigDecimal
545    }
546
547    /// Returns `None`
548    pub fn get_record_fields_in_ctxt(
549        _: &mut HashSet<Name>,
550        _: NamespaceRef,
551    ) -> Option<Vec<RecordField>> {
552        None
553    }
554
555    pub fn serialize<S>(decimal: &BigDecimal, serializer: S) -> Result<S::Ok, S::Error>
556    where
557        S: Serializer,
558    {
559        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
560        let decimal_bytes = big_decimal_as_bytes(decimal).map_err(S::Error::custom)?;
561        serde_bytes::serialize(&decimal_bytes, serializer)
562    }
563
564    pub fn deserialize<'de, D>(deserializer: D) -> Result<BigDecimal, D::Error>
565    where
566        D: Deserializer<'de>,
567    {
568        let _bytes_guard = super::BytesTypeGuard::set(BytesType::Bytes);
569        let _guard = super::BorrowedGuard::set(true);
570        // We don't use &'de [u8] here as the deserializer doesn't support that
571        let bytes: Vec<u8> = serde_bytes::deserialize(deserializer)?;
572
573        deserialize_big_decimal(&bytes).map_err(D::Error::custom)
574    }
575}
576
577/// (De)serialize [`Option<BigDecimal>`] as a `Schema::Union(Schema::Null, Schema::BigDecimal)` instead of a `Schema::Union(Schema::Null, Schema::String)`.
578///
579/// This module is intended to be used through the Serde `with` attribute.
580///
581/// Use [`apache_avro::serde::bigdecimal`] for non-optional big decimals values.
582///
583/// When used with different serialization formats, this will write bytes.
584///
585/// See usage with below example:
586/// ```
587/// # use apache_avro::{AvroSchema, BigDecimal};
588/// # use serde::{Deserialize, Serialize};
589/// #[derive(AvroSchema, Serialize, Deserialize)]
590/// struct StructWithBigDecimal {
591///     #[avro(with)]
592///     #[serde(with = "apache_avro::serde::bigdecimal_opt")]
593///     decimal: Option<BigDecimal>,
594/// }
595/// ```
596///
597/// [`Option<BigDecimal>`]: ::bigdecimal::BigDecimal
598/// [`apache_avro::serde::bigdecimal`]: bigdecimal
599pub mod bigdecimal_opt {
600    use std::collections::HashSet;
601
602    use bigdecimal::BigDecimal;
603    use serde::{Deserializer, Serializer, de::Error as _, ser::Error as _};
604
605    use crate::{
606        Schema,
607        bigdecimal::{big_decimal_as_bytes, deserialize_big_decimal},
608        schema::{Name, NamespaceRef, RecordField, UnionSchema},
609        serde::with::BytesType,
610    };
611
612    /// Returns `Schema::Union(Schema::Null, Schema::BigDecimal)`
613    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: NamespaceRef) -> Schema {
614        Schema::Union(
615            UnionSchema::new(vec![Schema::Null, Schema::BigDecimal])
616                .unwrap_or_else(|_| unreachable!("This is a valid union")),
617        )
618    }
619
620    /// Returns `None`
621    pub fn get_record_fields_in_ctxt(
622        _: &mut HashSet<Name>,
623        _: NamespaceRef,
624    ) -> Option<Vec<RecordField>> {
625        None
626    }
627
628    pub fn serialize<S>(decimal: &Option<BigDecimal>, serializer: S) -> Result<S::Ok, S::Error>
629    where
630        S: Serializer,
631    {
632        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
633        if let Some(decimal) = decimal {
634            let decimal_bytes = big_decimal_as_bytes(decimal).map_err(S::Error::custom)?;
635            serde_bytes::serialize(&Some(decimal_bytes), serializer)
636        } else {
637            serde_bytes::serialize(&None::<Vec<u8>>, serializer)
638        }
639    }
640
641    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<BigDecimal>, D::Error>
642    where
643        D: Deserializer<'de>,
644    {
645        let _bytes_guard = super::BytesTypeGuard::set(BytesType::Bytes);
646        let _guard = super::BorrowedGuard::set(true);
647        let bytes: Option<Vec<u8>> = serde_bytes::deserialize(deserializer)?;
648        if let Some(bytes) = bytes {
649            deserialize_big_decimal(&bytes)
650                .map(Some)
651                .map_err(D::Error::custom)
652        } else {
653            Ok(None)
654        }
655    }
656}
657
658/// (De)serialize an Rust array (`[T; N]`) as an Avro [`Schema::Array`].
659///
660/// This module is intended to be used through the Serde `with` attribute.
661///
662/// Use [`apache_avro::serde::array_opt`] for optional array values.
663///
664/// See usage with below example:
665/// ```
666/// # use apache_avro::AvroSchema;
667/// # use serde::{Deserialize, Serialize};
668/// #[derive(AvroSchema, Serialize, Deserialize)]
669/// struct StructWithArray {
670///     #[avro(with = apache_avro::serde::array::get_schema_in_ctxt::<i32>)]
671///     #[serde(with = "apache_avro::serde::array")]
672///     array: [i32; 10],
673/// }
674/// ```
675///
676/// [`apache_avro::serde::array_opt`]: array_opt
677/// [`Schema::Array`]: crate::schema::Schema::Array
678pub mod array {
679    use std::collections::HashSet;
680
681    use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
682
683    use crate::{
684        AvroSchemaComponent, Schema,
685        schema::{Name, NamespaceRef, RecordField},
686    };
687
688    /// Returns `Schema::Array(T::get_schema_in_ctxt())`
689    pub fn get_schema_in_ctxt<T: AvroSchemaComponent>(
690        named_schemas: &mut HashSet<Name>,
691        enclosing_namespace: NamespaceRef,
692    ) -> Schema {
693        Schema::array(T::get_schema_in_ctxt(named_schemas, enclosing_namespace)).build()
694    }
695
696    /// Returns `None`
697    pub fn get_record_fields_in_ctxt(
698        _: &mut HashSet<Name>,
699        _: NamespaceRef,
700    ) -> Option<Vec<RecordField>> {
701        None
702    }
703
704    pub fn serialize<const N: usize, S, T>(value: &[T; N], serializer: S) -> Result<S::Ok, S::Error>
705    where
706        S: Serializer,
707        T: Serialize,
708    {
709        value.as_slice().serialize(serializer)
710    }
711
712    pub fn deserialize<'de, const N: usize, D, T>(deserializer: D) -> Result<[T; N], D::Error>
713    where
714        D: Deserializer<'de>,
715        T: Deserialize<'de>,
716    {
717        let bytes = <Vec<T> as Deserialize>::deserialize(deserializer)?;
718        bytes.try_into().map_err(|v: Vec<T>| {
719            D::Error::custom(format!(
720                "Deserialized array has length {} which does not match array length of {N}",
721                v.len()
722            ))
723        })
724    }
725}
726
727/// (De)serialize an optional Rust array (`Option<[T; N]>`) as an Avro `Schema::Union([Schema::Null, Schema::Array])`.
728///
729/// This module is intended to be used through the Serde `with` attribute.
730///
731/// Use [`apache_avro::serde::array`] for non-optional array values.
732///
733/// See usage with below example:
734/// ```
735/// # use apache_avro::AvroSchema;
736/// # use serde::{Deserialize, Serialize};
737/// #[derive(AvroSchema, Serialize, Deserialize)]
738/// struct StructWithArray {
739///     #[avro(with = apache_avro::serde::array_opt::get_schema_in_ctxt::<i32>)]
740///     #[serde(with = "apache_avro::serde::array_opt")]
741///     array: Option<[i32; 10]>,
742/// }
743/// ```
744///
745/// [`apache_avro::serde::array`]: mod@array
746pub mod array_opt {
747    use std::collections::HashSet;
748
749    use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _};
750
751    use crate::{
752        AvroSchemaComponent, Schema,
753        schema::{Name, NamespaceRef, RecordField, UnionSchema},
754    };
755
756    /// Returns `Schema::Union(Schema::Null, Schema::Array(T::get_schema_in_ctxt()))`
757    pub fn get_schema_in_ctxt<T: AvroSchemaComponent>(
758        named_schemas: &mut HashSet<Name>,
759        enclosing_namespace: NamespaceRef,
760    ) -> Schema {
761        Schema::Union(
762            UnionSchema::new(vec![
763                Schema::Null,
764                Schema::array(T::get_schema_in_ctxt(named_schemas, enclosing_namespace)).build(),
765            ])
766            .unwrap_or_else(|_| unreachable!("This is a valid union")),
767        )
768    }
769
770    /// Returns `None`
771    pub fn get_record_fields_in_ctxt(
772        _: &mut HashSet<Name>,
773        _: NamespaceRef,
774    ) -> Option<Vec<RecordField>> {
775        None
776    }
777
778    pub fn serialize<const N: usize, S, T>(
779        value: &Option<[T; N]>,
780        serializer: S,
781    ) -> Result<S::Ok, S::Error>
782    where
783        S: Serializer,
784        T: Serialize,
785    {
786        if let Some(array) = value {
787            Some(array.as_slice()).serialize(serializer)
788        } else {
789            None::<Vec<T>>.serialize(serializer)
790        }
791    }
792
793    pub fn deserialize<'de, const N: usize, D, T>(
794        deserializer: D,
795    ) -> Result<Option<[T; N]>, D::Error>
796    where
797        D: Deserializer<'de>,
798        T: Deserialize<'de>,
799    {
800        let bytes = <Option<Vec<T>> as Deserialize>::deserialize(deserializer)?;
801        if let Some(bytes) = bytes {
802            Ok(Some(bytes.try_into().map_err(|v: Vec<T>| {
803                D::Error::custom(format!(
804                    "Deserialized array has length {} which does not match array length of {N}",
805                    v.len()
806                ))
807            })?))
808        } else {
809            Ok(None)
810        }
811    }
812}
813
814#[cfg(test)]
815mod tests {
816    use serde::{Deserialize, Serialize};
817
818    use crate::{Schema, from_value, to_value, types::Value};
819
820    #[test]
821    fn avro_3631_validate_schema_for_struct_with_byte_types() {
822        #[derive(Debug, Serialize)]
823        struct TestStructWithBytes<'a> {
824            #[serde(with = "crate::serde::bytes")]
825            vec_field: Vec<u8>,
826            #[serde(with = "crate::serde::bytes_opt")]
827            vec_field_opt: Option<Vec<u8>>,
828
829            #[serde(with = "crate::serde::fixed")]
830            fixed_field: [u8; 6],
831            #[serde(with = "crate::serde::fixed_opt")]
832            fixed_field_opt: Option<[u8; 7]>,
833
834            #[serde(with = "crate::serde::slice")]
835            slice_field: &'a [u8],
836            #[serde(with = "crate::serde::slice_opt")]
837            slice_field_opt: Option<&'a [u8]>,
838        }
839
840        let test = TestStructWithBytes {
841            vec_field: vec![2, 3, 4],
842            vec_field_opt: Some(vec![2, 3, 4]),
843            fixed_field: [1; 6],
844            fixed_field_opt: Some([1; 7]),
845            slice_field: &[1, 2, 3],
846            slice_field_opt: Some(&[1, 2, 3]),
847        };
848        let value: Value = to_value(test).unwrap();
849        let schema = Schema::parse_str(
850            r#"
851            {
852              "type": "record",
853              "name": "TestStructWithBytes",
854              "fields": [ {
855                "name": "vec_field",
856                "type": "bytes"
857              }, {
858                "name": "vec_field_opt",
859                "type": ["null", "bytes"]
860              }, {
861                "name": "fixed_field",
862                "type": {
863                  "name": "ByteData",
864                  "type": "fixed",
865                  "size": 6
866                }
867              }, {
868                "name": "fixed_field_opt",
869                "type": ["null", {
870                  "name": "ByteData2",
871                  "type": "fixed",
872                  "size": 7
873                } ]
874              }, {
875                "name": "slice_field",
876                "type": "bytes"
877              }, {
878                "name": "slice_field_opt",
879                "type": ["null", "bytes"]
880              } ]
881            }"#,
882        )
883        .unwrap();
884        assert!(value.validate(&schema));
885    }
886
887    #[test]
888    fn avro_3631_deserialize_value_to_struct_with_byte_types() {
889        #[derive(Debug, Deserialize, PartialEq)]
890        struct TestStructWithBytes<'a> {
891            #[serde(with = "crate::serde::bytes")]
892            vec_field: Vec<u8>,
893            #[serde(with = "crate::serde::bytes_opt")]
894            vec_field_opt: Option<Vec<u8>>,
895            #[serde(with = "crate::serde::bytes_opt")]
896            vec_field_opt2: Option<Vec<u8>>,
897
898            #[serde(with = "crate::serde::fixed")]
899            fixed_field: [u8; 6],
900            #[serde(with = "crate::serde::fixed_opt")]
901            fixed_field_opt: Option<[u8; 7]>,
902            #[serde(with = "crate::serde::fixed_opt")]
903            fixed_field_opt2: Option<[u8; 8]>,
904
905            #[serde(with = "crate::serde::slice")]
906            slice_bytes_field: &'a [u8],
907            #[serde(with = "crate::serde::slice_opt")]
908            slice_bytes_field_opt: Option<&'a [u8]>,
909            #[serde(with = "crate::serde::slice_opt")]
910            slice_bytes_field_opt2: Option<&'a [u8]>,
911
912            #[serde(with = "crate::serde::slice")]
913            slice_fixed_field: &'a [u8],
914            #[serde(with = "crate::serde::slice_opt")]
915            slice_fixed_field_opt: Option<&'a [u8]>,
916            #[serde(with = "crate::serde::slice_opt")]
917            slice_fixed_field_opt2: Option<&'a [u8]>,
918        }
919
920        let expected = TestStructWithBytes {
921            vec_field: vec![3, 33],
922            vec_field_opt: Some(vec![4, 44]),
923            vec_field_opt2: None,
924            fixed_field: [1; 6],
925            fixed_field_opt: Some([7; 7]),
926            fixed_field_opt2: None,
927            slice_bytes_field: &[1, 11, 111],
928            slice_bytes_field_opt: Some(&[5, 5, 5, 5, 5]),
929            slice_bytes_field_opt2: None,
930            slice_fixed_field: &[2, 22, 222],
931            slice_fixed_field_opt: Some(&[3, 3, 3]),
932            slice_fixed_field_opt2: None,
933        };
934
935        let value = Value::Record(vec![
936            (
937                "vec_field".to_owned(),
938                Value::Bytes(expected.vec_field.clone()),
939            ),
940            (
941                "vec_field_opt".to_owned(),
942                Value::Union(
943                    1,
944                    Box::new(Value::Bytes(
945                        expected.vec_field_opt.as_ref().unwrap().clone(),
946                    )),
947                ),
948            ),
949            (
950                "vec_field_opt2".to_owned(),
951                Value::Union(0, Box::new(Value::Null)),
952            ),
953            (
954                "fixed_field".to_owned(),
955                Value::Fixed(expected.fixed_field.len(), expected.fixed_field.to_vec()),
956            ),
957            (
958                "fixed_field_opt".to_owned(),
959                Value::Union(
960                    1,
961                    Box::new(Value::Fixed(
962                        expected.fixed_field_opt.as_ref().unwrap().len(),
963                        expected.fixed_field_opt.as_ref().unwrap().to_vec(),
964                    )),
965                ),
966            ),
967            (
968                "fixed_field_opt2".to_owned(),
969                Value::Union(0, Box::new(Value::Null)),
970            ),
971            (
972                "slice_bytes_field".to_owned(),
973                Value::Bytes(expected.slice_bytes_field.to_vec()),
974            ),
975            (
976                "slice_bytes_field_opt".to_owned(),
977                Value::Union(
978                    1,
979                    Box::new(Value::Bytes(
980                        expected.slice_bytes_field_opt.as_ref().unwrap().to_vec(),
981                    )),
982                ),
983            ),
984            (
985                "slice_bytes_field_opt2".to_owned(),
986                Value::Union(0, Box::new(Value::Null)),
987            ),
988            (
989                "slice_fixed_field".to_owned(),
990                Value::Fixed(
991                    expected.slice_fixed_field.len(),
992                    expected.slice_fixed_field.to_vec(),
993                ),
994            ),
995            (
996                "slice_fixed_field_opt".to_owned(),
997                Value::Union(
998                    1,
999                    Box::new(Value::Fixed(
1000                        expected.slice_fixed_field_opt.as_ref().unwrap().len(),
1001                        expected.slice_fixed_field_opt.as_ref().unwrap().to_vec(),
1002                    )),
1003                ),
1004            ),
1005            (
1006                "slice_fixed_field_opt2".to_owned(),
1007                Value::Union(1, Box::new(Value::Null)),
1008            ),
1009        ]);
1010        assert_eq!(expected, from_value(&value).unwrap());
1011    }
1012
1013    #[test]
1014    fn avro_3631_serialize_struct_to_value_with_byte_types() {
1015        #[derive(Debug, Serialize)]
1016        struct TestStructWithBytes<'a> {
1017            array_field: &'a [u8],
1018            vec_field: Vec<u8>,
1019
1020            #[serde(with = "crate::serde::fixed")]
1021            vec_field2: Vec<u8>,
1022            #[serde(with = "crate::serde::fixed_opt")]
1023            vec_field2_opt: Option<Vec<u8>>,
1024            #[serde(with = "crate::serde::fixed_opt")]
1025            vec_field2_opt2: Option<Vec<u8>>,
1026
1027            #[serde(with = "crate::serde::bytes")]
1028            vec_field3: Vec<u8>,
1029            #[serde(with = "crate::serde::bytes_opt")]
1030            vec_field3_opt: Option<Vec<u8>>,
1031            #[serde(with = "crate::serde::bytes_opt")]
1032            vec_field3_opt2: Option<Vec<u8>>,
1033
1034            #[serde(with = "crate::serde::fixed")]
1035            fixed_field: [u8; 6],
1036            #[serde(with = "crate::serde::fixed_opt")]
1037            fixed_field_opt: Option<[u8; 5]>,
1038            #[serde(with = "crate::serde::fixed_opt")]
1039            fixed_field_opt2: Option<[u8; 4]>,
1040
1041            #[serde(with = "crate::serde::fixed")]
1042            fixed_field2: &'a [u8],
1043            #[serde(with = "crate::serde::fixed_opt")]
1044            fixed_field2_opt: Option<&'a [u8]>,
1045            #[serde(with = "crate::serde::fixed_opt")]
1046            fixed_field2_opt2: Option<&'a [u8]>,
1047
1048            #[serde(with = "crate::serde::bytes")]
1049            bytes_field: &'a [u8],
1050            #[serde(with = "crate::serde::bytes_opt")]
1051            bytes_field_opt: Option<&'a [u8]>,
1052            #[serde(with = "crate::serde::bytes_opt")]
1053            bytes_field_opt2: Option<&'a [u8]>,
1054
1055            #[serde(with = "crate::serde::bytes")]
1056            bytes_field2: [u8; 6],
1057            #[serde(with = "crate::serde::bytes_opt")]
1058            bytes_field2_opt: Option<[u8; 7]>,
1059            #[serde(with = "crate::serde::bytes_opt")]
1060            bytes_field2_opt2: Option<[u8; 8]>,
1061        }
1062
1063        let test = TestStructWithBytes {
1064            array_field: &[1, 11, 111],
1065            vec_field: vec![3, 33],
1066            vec_field2: vec![4, 44],
1067            vec_field2_opt: Some(vec![14, 144]),
1068            vec_field2_opt2: None,
1069            vec_field3: vec![5, 55],
1070            vec_field3_opt: Some(vec![15, 155]),
1071            vec_field3_opt2: None,
1072            fixed_field: [1; 6],
1073            fixed_field_opt: Some([6; 5]),
1074            fixed_field_opt2: None,
1075            fixed_field2: &[6, 66],
1076            fixed_field2_opt: Some(&[7, 77]),
1077            fixed_field2_opt2: None,
1078            bytes_field: &[2, 22, 222],
1079            bytes_field_opt: Some(&[3, 33, 233]),
1080            bytes_field_opt2: None,
1081            bytes_field2: [2; 6],
1082            bytes_field2_opt: Some([2; 7]),
1083            bytes_field2_opt2: None,
1084        };
1085        let expected = Value::Record(vec![
1086            (
1087                "array_field".to_owned(),
1088                Value::Array(
1089                    test.array_field
1090                        .iter()
1091                        .map(|&i| Value::Int(i as i32))
1092                        .collect(),
1093                ),
1094            ),
1095            (
1096                "vec_field".to_owned(),
1097                Value::Array(
1098                    test.vec_field
1099                        .iter()
1100                        .map(|&i| Value::Int(i as i32))
1101                        .collect(),
1102                ),
1103            ),
1104            (
1105                "vec_field2".to_owned(),
1106                Value::Fixed(test.vec_field2.len(), test.vec_field2.clone()),
1107            ),
1108            (
1109                "vec_field2_opt".to_owned(),
1110                Value::Union(
1111                    1,
1112                    Box::new(Value::Fixed(
1113                        test.vec_field2_opt.as_ref().unwrap().len(),
1114                        test.vec_field2_opt.as_ref().unwrap().to_vec(),
1115                    )),
1116                ),
1117            ),
1118            (
1119                "vec_field2_opt2".to_owned(),
1120                Value::Union(0, Box::new(Value::Null)),
1121            ),
1122            (
1123                "vec_field3".to_owned(),
1124                Value::Bytes(test.vec_field3.clone()),
1125            ),
1126            (
1127                "vec_field3_opt".to_owned(),
1128                Value::Union(
1129                    1,
1130                    Box::new(Value::Bytes(test.vec_field3_opt.as_ref().unwrap().clone())),
1131                ),
1132            ),
1133            (
1134                "vec_field3_opt2".to_owned(),
1135                Value::Union(0, Box::new(Value::Null)),
1136            ),
1137            (
1138                "fixed_field".to_owned(),
1139                Value::Fixed(test.fixed_field.len(), test.fixed_field.to_vec()),
1140            ),
1141            (
1142                "fixed_field_opt".to_owned(),
1143                Value::Union(
1144                    1,
1145                    Box::new(Value::Fixed(
1146                        test.fixed_field_opt.as_ref().unwrap().len(),
1147                        test.fixed_field_opt.as_ref().unwrap().to_vec(),
1148                    )),
1149                ),
1150            ),
1151            (
1152                "fixed_field_opt2".to_owned(),
1153                Value::Union(0, Box::new(Value::Null)),
1154            ),
1155            (
1156                "fixed_field2".to_owned(),
1157                Value::Fixed(test.fixed_field2.len(), test.fixed_field2.to_vec()),
1158            ),
1159            (
1160                "fixed_field2_opt".to_owned(),
1161                Value::Union(
1162                    1,
1163                    Box::new(Value::Fixed(
1164                        test.fixed_field2_opt.as_ref().unwrap().len(),
1165                        test.fixed_field2_opt.as_ref().unwrap().to_vec(),
1166                    )),
1167                ),
1168            ),
1169            (
1170                "fixed_field2_opt2".to_owned(),
1171                Value::Union(0, Box::new(Value::Null)),
1172            ),
1173            (
1174                "bytes_field".to_owned(),
1175                Value::Bytes(test.bytes_field.to_vec()),
1176            ),
1177            (
1178                "bytes_field_opt".to_owned(),
1179                Value::Union(
1180                    1,
1181                    Box::new(Value::Bytes(
1182                        test.bytes_field_opt.as_ref().unwrap().to_vec(),
1183                    )),
1184                ),
1185            ),
1186            (
1187                "bytes_field_opt2".to_owned(),
1188                Value::Union(0, Box::new(Value::Null)),
1189            ),
1190            (
1191                "bytes_field2".to_owned(),
1192                Value::Bytes(test.bytes_field2.to_vec()),
1193            ),
1194            (
1195                "bytes_field2_opt".to_owned(),
1196                Value::Union(
1197                    1,
1198                    Box::new(Value::Bytes(
1199                        test.bytes_field2_opt.as_ref().unwrap().to_vec(),
1200                    )),
1201                ),
1202            ),
1203            (
1204                "bytes_field2_opt2".to_owned(),
1205                Value::Union(0, Box::new(Value::Null)),
1206            ),
1207        ]);
1208        assert_eq!(expected, to_value(test).unwrap());
1209    }
1210}