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 super::BytesType;
97    use std::collections::HashSet;
98
99    use serde::{Deserializer, Serializer};
100
101    use crate::schema::NamespaceRef;
102    use crate::{
103        Schema,
104        schema::{Name, RecordField},
105    };
106
107    /// Returns [`Schema::Bytes`]
108    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: NamespaceRef) -> Schema {
109        Schema::Bytes
110    }
111
112    /// Returns `None`
113    pub fn get_record_fields_in_ctxt(
114        _: usize,
115        _: &mut HashSet<Name>,
116        _: NamespaceRef,
117    ) -> Option<Vec<RecordField>> {
118        None
119    }
120
121    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
122    where
123        S: Serializer,
124    {
125        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
126        serde_bytes::serialize(bytes, serializer)
127    }
128
129    pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
130    where
131        D: Deserializer<'de>,
132    {
133        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
134        serde_bytes::deserialize(deserializer)
135    }
136}
137
138/// Efficient (de)serialization of optional Avro bytes values.
139///
140/// This module is intended to be used through the Serde `with` attribute.
141/// Use [`apache_avro::serde::bytes`] for non-optional bytes.
142///
143/// When used with different serialization formats, this is equivalent to [`serde_bytes`].
144///
145/// See usage with below example:
146/// ```
147/// # use apache_avro::AvroSchema;
148/// # use serde::{Deserialize, Serialize};
149/// #[derive(AvroSchema, Serialize, Deserialize)]
150/// struct StructWithBytes {
151///     #[avro(with)]
152///     #[serde(with = "apache_avro::serde::bytes_opt")]
153///     vec_field: Option<Vec<u8>>,
154///
155///     #[avro(with = apache_avro::serde::fixed_opt::get_schema_in_ctxt::<6>)]
156///     #[serde(with = "apache_avro::serde::fixed_opt")]
157///     fixed_field: Option<[u8; 6]>,
158/// }
159/// ```
160///
161/// [`apache_avro::serde::bytes`]: bytes
162pub mod bytes_opt {
163    use super::BytesType;
164    use serde::{Deserializer, Serializer};
165    use std::{borrow::Borrow, collections::HashSet};
166
167    use crate::schema::NamespaceRef;
168    use crate::{
169        Schema,
170        schema::{Name, RecordField, UnionSchema},
171    };
172
173    /// Returns `Schema::Union(Schema::Null, Schema::Bytes)`
174    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: NamespaceRef) -> Schema {
175        Schema::Union(
176            UnionSchema::new(vec![Schema::Null, Schema::Bytes]).expect("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 super::BytesType;
235    use serde::{Deserializer, Serializer};
236
237    use crate::schema::NamespaceRef;
238    use crate::{
239        Schema,
240        schema::{FixedSchema, Name, RecordField},
241    };
242
243    /// Returns `Schema::Fixed(N)` named `serde_avro_fixed_{N}`
244    pub fn get_schema_in_ctxt<const N: usize>(
245        named_schemas: &mut HashSet<Name>,
246        enclosing_namespace: NamespaceRef,
247    ) -> Schema {
248        let name = Name::new_with_enclosing_namespace(
249            format!("serde_avro_fixed_{N}"),
250            enclosing_namespace,
251        )
252        .expect("Name is valid");
253        if named_schemas.contains(&name) {
254            Schema::Ref { name }
255        } else {
256            let schema = Schema::Fixed(FixedSchema::builder().name(name.clone()).size(N).build());
257            named_schemas.insert(name);
258            schema
259        }
260    }
261
262    /// Returns `None`
263    pub fn get_record_fields_in_ctxt(
264        _: usize,
265        _: &mut HashSet<Name>,
266        _: NamespaceRef,
267    ) -> Option<Vec<RecordField>> {
268        None
269    }
270
271    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
272    where
273        S: Serializer,
274    {
275        let _guard = super::BytesTypeGuard::set(BytesType::Fixed);
276        serde_bytes::serialize(bytes, serializer)
277    }
278
279    pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
280    where
281        D: Deserializer<'de>,
282    {
283        let _guard = super::BytesTypeGuard::set(BytesType::Fixed);
284        serde_bytes::deserialize(deserializer)
285    }
286}
287
288/// Efficient (de)serialization of optional Avro fixed values.
289///
290/// This module is intended to be used through the Serde `with` attribute.
291/// Use [`apache_avro::serde::fixed`] for non-optional fixed values.
292///
293/// When used with different serialization formats, this is equivalent to [`serde_bytes`].
294///
295/// See usage with below example:
296/// ```
297/// # use apache_avro::AvroSchema;
298/// # use serde::{Deserialize, Serialize};
299/// #[derive(AvroSchema, Serialize, Deserialize)]
300/// struct StructWithBytes {
301///     #[avro(with)]
302///     #[serde(with = "apache_avro::serde::bytes_opt")]
303///     vec_field: Option<Vec<u8>>,
304///
305///     #[avro(with = apache_avro::serde::fixed_opt::get_schema_in_ctxt::<6>)]
306///     #[serde(with = "apache_avro::serde::fixed_opt")]
307///     fixed_field: Option<[u8; 6]>,
308/// }
309/// ```
310///
311/// [`apache_avro::serde::fixed`]: fixed
312pub mod fixed_opt {
313    use super::BytesType;
314    use serde::{Deserializer, Serializer};
315    use std::{borrow::Borrow, collections::HashSet};
316
317    use crate::schema::NamespaceRef;
318    use crate::{
319        Schema,
320        schema::{Name, RecordField, UnionSchema},
321    };
322
323    /// Returns `Schema::Union(Schema::Null, Schema::Fixed(N))` where the fixed schema is named `serde_avro_fixed_{N}`
324    pub fn get_schema_in_ctxt<const N: usize>(
325        named_schemas: &mut HashSet<Name>,
326        enclosing_namespace: NamespaceRef,
327    ) -> Schema {
328        Schema::Union(
329            UnionSchema::new(vec![
330                Schema::Null,
331                super::fixed::get_schema_in_ctxt::<N>(named_schemas, enclosing_namespace),
332            ])
333            .expect("This is a valid union"),
334        )
335    }
336
337    /// Returns `None`
338    pub fn get_record_fields_in_ctxt(
339        _: usize,
340        _: &mut HashSet<Name>,
341        _: NamespaceRef,
342    ) -> Option<Vec<RecordField>> {
343        None
344    }
345
346    pub fn serialize<S, B>(bytes: &Option<B>, serializer: S) -> Result<S::Ok, S::Error>
347    where
348        S: Serializer,
349        B: Borrow<[u8]> + serde_bytes::Serialize,
350    {
351        let _guard = super::BytesTypeGuard::set(BytesType::Fixed);
352        serde_bytes::serialize(bytes, serializer)
353    }
354
355    pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<Option<[u8; N]>, D::Error>
356    where
357        D: Deserializer<'de>,
358    {
359        let _guard = super::BytesTypeGuard::set(BytesType::Fixed);
360        serde_bytes::deserialize(deserializer)
361    }
362}
363
364/// Efficient (de)serialization of Avro bytes/fixed borrowed values.
365///
366/// This module is intended to be used through the Serde `with` attribute.
367///
368/// Note that `&[u8]` are always serialized as [`Value::Bytes`]. However,
369/// both [`Value::Bytes`] and [`Value::Fixed`] can be deserialized as `&[u8]`.
370///
371/// Use [`apache_avro::serde::slice_opt`] for optional bytes/fixed borrowed values.
372///
373/// When used with different serialization formats, this is equivalent to [`serde_bytes`].
374///
375/// See usage with below example:
376/// ```
377/// # use apache_avro::AvroSchema;
378/// # use serde::{Deserialize, Serialize};
379/// #[derive(AvroSchema, Serialize, Deserialize)]
380/// struct StructWithBytes<'a> {
381///     #[avro(with)]
382///     #[serde(with = "apache_avro::serde::slice")]
383///     slice_field: &'a [u8],
384/// }
385/// ```
386///
387/// [`Value::Bytes`]: crate::types::Value::Bytes
388/// [`Value::Fixed`]: crate::types::Value::Fixed
389/// [`apache_avro::serde::slice_opt`]: slice_opt
390pub mod slice {
391    use super::BytesType;
392    use std::collections::HashSet;
393
394    use serde::{Deserializer, Serializer};
395
396    use crate::schema::NamespaceRef;
397    use crate::{
398        Schema,
399        schema::{Name, RecordField},
400    };
401
402    /// Returns [`Schema::Bytes`]
403    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: NamespaceRef) -> Schema {
404        Schema::Bytes
405    }
406
407    /// Returns `None`
408    pub fn get_record_fields_in_ctxt(
409        _: usize,
410        _: &mut HashSet<Name>,
411        _: NamespaceRef,
412    ) -> Option<Vec<RecordField>> {
413        None
414    }
415
416    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
417    where
418        S: Serializer,
419    {
420        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
421        serde_bytes::serialize(bytes, serializer)
422    }
423
424    pub fn deserialize<'de, D>(deserializer: D) -> Result<&'de [u8], D::Error>
425    where
426        D: Deserializer<'de>,
427    {
428        let _bytes_guard = super::BytesTypeGuard::set(BytesType::Bytes);
429        let _guard = super::BorrowedGuard::set(true);
430        serde_bytes::deserialize(deserializer)
431    }
432}
433
434/// Efficient (de)serialization of optional Avro bytes/fixed borrowed values.
435///
436/// This module is intended to be used through the Serde `with` attribute.
437///
438/// Note that `&[u8]` are always serialized as [`Value::Bytes`]. However,
439/// both [`Value::Bytes`] and [`Value::Fixed`] can be deserialized as `&[u8]`.
440///
441/// Use [`apache_avro::serde::slice`] for non-optional bytes/fixed borrowed values.
442///
443/// When used with different serialization formats, this is equivalent to [`serde_bytes`].
444///
445/// See usage with below example:
446/// ```
447/// # use apache_avro::AvroSchema;
448/// # use serde::{Deserialize, Serialize};
449/// #[derive(AvroSchema, Serialize, Deserialize)]
450/// struct StructWithBytes<'a> {
451///     #[avro(with)]
452///     #[serde(with = "apache_avro::serde::slice_opt")]
453///     slice_field: Option<&'a [u8]>,
454/// }
455/// ```
456///
457/// [`Value::Bytes`]: crate::types::Value::Bytes
458/// [`Value::Fixed`]: crate::types::Value::Fixed
459/// [`apache_avro::serde::slice`]: mod@slice
460pub mod slice_opt {
461    use super::BytesType;
462    use serde::{Deserializer, Serializer};
463    use std::{borrow::Borrow, collections::HashSet};
464
465    use crate::schema::NamespaceRef;
466    use crate::{
467        Schema,
468        schema::{Name, RecordField, UnionSchema},
469    };
470
471    /// Returns `Schema::Union(Schema::Null, Schema::Bytes)`
472    pub fn get_schema_in_ctxt(_: &mut HashSet<Name>, _: NamespaceRef) -> Schema {
473        Schema::Union(
474            UnionSchema::new(vec![Schema::Null, Schema::Bytes]).expect("This is a valid union"),
475        )
476    }
477
478    /// Returns `None`
479    pub fn get_record_fields_in_ctxt(
480        _: usize,
481        _: &mut HashSet<Name>,
482        _: NamespaceRef,
483    ) -> Option<Vec<RecordField>> {
484        None
485    }
486
487    pub fn serialize<S, B>(bytes: &Option<B>, serializer: S) -> Result<S::Ok, S::Error>
488    where
489        S: Serializer,
490        B: Borrow<[u8]> + serde_bytes::Serialize,
491    {
492        let _guard = super::BytesTypeGuard::set(BytesType::Bytes);
493        serde_bytes::serialize(&bytes, serializer)
494    }
495
496    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<&'de [u8]>, D::Error>
497    where
498        D: Deserializer<'de>,
499    {
500        let _bytes_guard = super::BytesTypeGuard::set(BytesType::Bytes);
501        let _guard = super::BorrowedGuard::set(true);
502        serde_bytes::deserialize(deserializer)
503    }
504}
505
506#[cfg(test)]
507mod tests {
508    use crate::{Schema, from_value, to_value, types::Value};
509    use serde::{Deserialize, Serialize};
510
511    #[test]
512    fn avro_3631_validate_schema_for_struct_with_byte_types() {
513        #[derive(Debug, Serialize)]
514        struct TestStructWithBytes<'a> {
515            #[serde(with = "crate::serde::bytes")]
516            vec_field: Vec<u8>,
517            #[serde(with = "crate::serde::bytes_opt")]
518            vec_field_opt: Option<Vec<u8>>,
519
520            #[serde(with = "crate::serde::fixed")]
521            fixed_field: [u8; 6],
522            #[serde(with = "crate::serde::fixed_opt")]
523            fixed_field_opt: Option<[u8; 7]>,
524
525            #[serde(with = "crate::serde::slice")]
526            slice_field: &'a [u8],
527            #[serde(with = "crate::serde::slice_opt")]
528            slice_field_opt: Option<&'a [u8]>,
529        }
530
531        let test = TestStructWithBytes {
532            vec_field: vec![2, 3, 4],
533            vec_field_opt: Some(vec![2, 3, 4]),
534            fixed_field: [1; 6],
535            fixed_field_opt: Some([1; 7]),
536            slice_field: &[1, 2, 3],
537            slice_field_opt: Some(&[1, 2, 3]),
538        };
539        let value: Value = to_value(test).unwrap();
540        let schema = Schema::parse_str(
541            r#"
542            {
543              "type": "record",
544              "name": "TestStructWithBytes",
545              "fields": [ {
546                "name": "vec_field",
547                "type": "bytes"
548              }, {
549                "name": "vec_field_opt",
550                "type": ["null", "bytes"]
551              }, {
552                "name": "fixed_field",
553                "type": {
554                  "name": "ByteData",
555                  "type": "fixed",
556                  "size": 6
557                }
558              }, {
559                "name": "fixed_field_opt",
560                "type": ["null", {
561                  "name": "ByteData2",
562                  "type": "fixed",
563                  "size": 7
564                } ]
565              }, {
566                "name": "slice_field",
567                "type": "bytes"
568              }, {
569                "name": "slice_field_opt",
570                "type": ["null", "bytes"]
571              } ]
572            }"#,
573        )
574        .unwrap();
575        assert!(value.validate(&schema));
576    }
577
578    #[test]
579    fn avro_3631_deserialize_value_to_struct_with_byte_types() {
580        #[derive(Debug, Deserialize, PartialEq)]
581        struct TestStructWithBytes<'a> {
582            #[serde(with = "crate::serde::bytes")]
583            vec_field: Vec<u8>,
584            #[serde(with = "crate::serde::bytes_opt")]
585            vec_field_opt: Option<Vec<u8>>,
586            #[serde(with = "crate::serde::bytes_opt")]
587            vec_field_opt2: Option<Vec<u8>>,
588
589            #[serde(with = "crate::serde::fixed")]
590            fixed_field: [u8; 6],
591            #[serde(with = "crate::serde::fixed_opt")]
592            fixed_field_opt: Option<[u8; 7]>,
593            #[serde(with = "crate::serde::fixed_opt")]
594            fixed_field_opt2: Option<[u8; 8]>,
595
596            #[serde(with = "crate::serde::slice")]
597            slice_bytes_field: &'a [u8],
598            #[serde(with = "crate::serde::slice_opt")]
599            slice_bytes_field_opt: Option<&'a [u8]>,
600            #[serde(with = "crate::serde::slice_opt")]
601            slice_bytes_field_opt2: Option<&'a [u8]>,
602
603            #[serde(with = "crate::serde::slice")]
604            slice_fixed_field: &'a [u8],
605            #[serde(with = "crate::serde::slice_opt")]
606            slice_fixed_field_opt: Option<&'a [u8]>,
607            #[serde(with = "crate::serde::slice_opt")]
608            slice_fixed_field_opt2: Option<&'a [u8]>,
609        }
610
611        let expected = TestStructWithBytes {
612            vec_field: vec![3, 33],
613            vec_field_opt: Some(vec![4, 44]),
614            vec_field_opt2: None,
615            fixed_field: [1; 6],
616            fixed_field_opt: Some([7; 7]),
617            fixed_field_opt2: None,
618            slice_bytes_field: &[1, 11, 111],
619            slice_bytes_field_opt: Some(&[5, 5, 5, 5, 5]),
620            slice_bytes_field_opt2: None,
621            slice_fixed_field: &[2, 22, 222],
622            slice_fixed_field_opt: Some(&[3, 3, 3]),
623            slice_fixed_field_opt2: None,
624        };
625
626        let value = Value::Record(vec![
627            (
628                "vec_field".to_owned(),
629                Value::Bytes(expected.vec_field.clone()),
630            ),
631            (
632                "vec_field_opt".to_owned(),
633                Value::Union(
634                    1,
635                    Box::new(Value::Bytes(
636                        expected.vec_field_opt.as_ref().unwrap().clone(),
637                    )),
638                ),
639            ),
640            (
641                "vec_field_opt2".to_owned(),
642                Value::Union(0, Box::new(Value::Null)),
643            ),
644            (
645                "fixed_field".to_owned(),
646                Value::Fixed(expected.fixed_field.len(), expected.fixed_field.to_vec()),
647            ),
648            (
649                "fixed_field_opt".to_owned(),
650                Value::Union(
651                    1,
652                    Box::new(Value::Fixed(
653                        expected.fixed_field_opt.as_ref().unwrap().len(),
654                        expected.fixed_field_opt.as_ref().unwrap().to_vec(),
655                    )),
656                ),
657            ),
658            (
659                "fixed_field_opt2".to_owned(),
660                Value::Union(0, Box::new(Value::Null)),
661            ),
662            (
663                "slice_bytes_field".to_owned(),
664                Value::Bytes(expected.slice_bytes_field.to_vec()),
665            ),
666            (
667                "slice_bytes_field_opt".to_owned(),
668                Value::Union(
669                    1,
670                    Box::new(Value::Bytes(
671                        expected.slice_bytes_field_opt.as_ref().unwrap().to_vec(),
672                    )),
673                ),
674            ),
675            (
676                "slice_bytes_field_opt2".to_owned(),
677                Value::Union(0, Box::new(Value::Null)),
678            ),
679            (
680                "slice_fixed_field".to_owned(),
681                Value::Fixed(
682                    expected.slice_fixed_field.len(),
683                    expected.slice_fixed_field.to_vec(),
684                ),
685            ),
686            (
687                "slice_fixed_field_opt".to_owned(),
688                Value::Union(
689                    1,
690                    Box::new(Value::Fixed(
691                        expected.slice_fixed_field_opt.as_ref().unwrap().len(),
692                        expected.slice_fixed_field_opt.as_ref().unwrap().to_vec(),
693                    )),
694                ),
695            ),
696            (
697                "slice_fixed_field_opt2".to_owned(),
698                Value::Union(1, Box::new(Value::Null)),
699            ),
700        ]);
701        assert_eq!(expected, from_value(&value).unwrap());
702    }
703
704    #[test]
705    fn avro_3631_serialize_struct_to_value_with_byte_types() {
706        #[derive(Debug, Serialize)]
707        struct TestStructWithBytes<'a> {
708            array_field: &'a [u8],
709            vec_field: Vec<u8>,
710
711            #[serde(with = "crate::serde::fixed")]
712            vec_field2: Vec<u8>,
713            #[serde(with = "crate::serde::fixed_opt")]
714            vec_field2_opt: Option<Vec<u8>>,
715            #[serde(with = "crate::serde::fixed_opt")]
716            vec_field2_opt2: Option<Vec<u8>>,
717
718            #[serde(with = "crate::serde::bytes")]
719            vec_field3: Vec<u8>,
720            #[serde(with = "crate::serde::bytes_opt")]
721            vec_field3_opt: Option<Vec<u8>>,
722            #[serde(with = "crate::serde::bytes_opt")]
723            vec_field3_opt2: Option<Vec<u8>>,
724
725            #[serde(with = "crate::serde::fixed")]
726            fixed_field: [u8; 6],
727            #[serde(with = "crate::serde::fixed_opt")]
728            fixed_field_opt: Option<[u8; 5]>,
729            #[serde(with = "crate::serde::fixed_opt")]
730            fixed_field_opt2: Option<[u8; 4]>,
731
732            #[serde(with = "crate::serde::fixed")]
733            fixed_field2: &'a [u8],
734            #[serde(with = "crate::serde::fixed_opt")]
735            fixed_field2_opt: Option<&'a [u8]>,
736            #[serde(with = "crate::serde::fixed_opt")]
737            fixed_field2_opt2: Option<&'a [u8]>,
738
739            #[serde(with = "crate::serde::bytes")]
740            bytes_field: &'a [u8],
741            #[serde(with = "crate::serde::bytes_opt")]
742            bytes_field_opt: Option<&'a [u8]>,
743            #[serde(with = "crate::serde::bytes_opt")]
744            bytes_field_opt2: Option<&'a [u8]>,
745
746            #[serde(with = "crate::serde::bytes")]
747            bytes_field2: [u8; 6],
748            #[serde(with = "crate::serde::bytes_opt")]
749            bytes_field2_opt: Option<[u8; 7]>,
750            #[serde(with = "crate::serde::bytes_opt")]
751            bytes_field2_opt2: Option<[u8; 8]>,
752        }
753
754        let test = TestStructWithBytes {
755            array_field: &[1, 11, 111],
756            vec_field: vec![3, 33],
757            vec_field2: vec![4, 44],
758            vec_field2_opt: Some(vec![14, 144]),
759            vec_field2_opt2: None,
760            vec_field3: vec![5, 55],
761            vec_field3_opt: Some(vec![15, 155]),
762            vec_field3_opt2: None,
763            fixed_field: [1; 6],
764            fixed_field_opt: Some([6; 5]),
765            fixed_field_opt2: None,
766            fixed_field2: &[6, 66],
767            fixed_field2_opt: Some(&[7, 77]),
768            fixed_field2_opt2: None,
769            bytes_field: &[2, 22, 222],
770            bytes_field_opt: Some(&[3, 33, 233]),
771            bytes_field_opt2: None,
772            bytes_field2: [2; 6],
773            bytes_field2_opt: Some([2; 7]),
774            bytes_field2_opt2: None,
775        };
776        let expected = Value::Record(vec![
777            (
778                "array_field".to_owned(),
779                Value::Array(
780                    test.array_field
781                        .iter()
782                        .map(|&i| Value::Int(i as i32))
783                        .collect(),
784                ),
785            ),
786            (
787                "vec_field".to_owned(),
788                Value::Array(
789                    test.vec_field
790                        .iter()
791                        .map(|&i| Value::Int(i as i32))
792                        .collect(),
793                ),
794            ),
795            (
796                "vec_field2".to_owned(),
797                Value::Fixed(test.vec_field2.len(), test.vec_field2.clone()),
798            ),
799            (
800                "vec_field2_opt".to_owned(),
801                Value::Union(
802                    1,
803                    Box::new(Value::Fixed(
804                        test.vec_field2_opt.as_ref().unwrap().len(),
805                        test.vec_field2_opt.as_ref().unwrap().to_vec(),
806                    )),
807                ),
808            ),
809            (
810                "vec_field2_opt2".to_owned(),
811                Value::Union(0, Box::new(Value::Null)),
812            ),
813            (
814                "vec_field3".to_owned(),
815                Value::Bytes(test.vec_field3.clone()),
816            ),
817            (
818                "vec_field3_opt".to_owned(),
819                Value::Union(
820                    1,
821                    Box::new(Value::Bytes(test.vec_field3_opt.as_ref().unwrap().clone())),
822                ),
823            ),
824            (
825                "vec_field3_opt2".to_owned(),
826                Value::Union(0, Box::new(Value::Null)),
827            ),
828            (
829                "fixed_field".to_owned(),
830                Value::Fixed(test.fixed_field.len(), test.fixed_field.to_vec()),
831            ),
832            (
833                "fixed_field_opt".to_owned(),
834                Value::Union(
835                    1,
836                    Box::new(Value::Fixed(
837                        test.fixed_field_opt.as_ref().unwrap().len(),
838                        test.fixed_field_opt.as_ref().unwrap().to_vec(),
839                    )),
840                ),
841            ),
842            (
843                "fixed_field_opt2".to_owned(),
844                Value::Union(0, Box::new(Value::Null)),
845            ),
846            (
847                "fixed_field2".to_owned(),
848                Value::Fixed(test.fixed_field2.len(), test.fixed_field2.to_vec()),
849            ),
850            (
851                "fixed_field2_opt".to_owned(),
852                Value::Union(
853                    1,
854                    Box::new(Value::Fixed(
855                        test.fixed_field2_opt.as_ref().unwrap().len(),
856                        test.fixed_field2_opt.as_ref().unwrap().to_vec(),
857                    )),
858                ),
859            ),
860            (
861                "fixed_field2_opt2".to_owned(),
862                Value::Union(0, Box::new(Value::Null)),
863            ),
864            (
865                "bytes_field".to_owned(),
866                Value::Bytes(test.bytes_field.to_vec()),
867            ),
868            (
869                "bytes_field_opt".to_owned(),
870                Value::Union(
871                    1,
872                    Box::new(Value::Bytes(
873                        test.bytes_field_opt.as_ref().unwrap().to_vec(),
874                    )),
875                ),
876            ),
877            (
878                "bytes_field_opt2".to_owned(),
879                Value::Union(0, Box::new(Value::Null)),
880            ),
881            (
882                "bytes_field2".to_owned(),
883                Value::Bytes(test.bytes_field2.to_vec()),
884            ),
885            (
886                "bytes_field2_opt".to_owned(),
887                Value::Union(
888                    1,
889                    Box::new(Value::Bytes(
890                        test.bytes_field2_opt.as_ref().unwrap().to_vec(),
891                    )),
892                ),
893            ),
894            (
895                "bytes_field2_opt2".to_owned(),
896                Value::Union(0, Box::new(Value::Null)),
897            ),
898        ]);
899        assert_eq!(expected, to_value(test).unwrap());
900    }
901}