tinystr/
serde.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5use crate::TinyAsciiStr;
6use alloc::borrow::Cow;
7use alloc::string::ToString;
8use core::fmt;
9use core::marker::PhantomData;
10use core::ops::Deref;
11use serde::de::{Error, SeqAccess, Visitor};
12use serde::ser::SerializeTuple;
13use serde::{Deserialize, Deserializer, Serialize, Serializer};
14
15impl<const N: usize> Serialize for TinyAsciiStr<N> {
16    #[inline]
17    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
18    where
19        S: Serializer,
20    {
21        if serializer.is_human_readable() {
22            self.deref().serialize(serializer)
23        } else {
24            let mut seq = serializer.serialize_tuple(N)?;
25            for byte in self.all_bytes() {
26                seq.serialize_element(byte)?;
27            }
28            seq.end()
29        }
30    }
31}
32
33struct TinyAsciiStrVisitor<const N: usize> {
34    marker: PhantomData<TinyAsciiStr<N>>,
35}
36
37impl<const N: usize> TinyAsciiStrVisitor<N> {
38    fn new() -> Self {
39        TinyAsciiStrVisitor {
40            marker: PhantomData,
41        }
42    }
43}
44
45impl<'de, const N: usize> Visitor<'de> for TinyAsciiStrVisitor<N> {
46    type Value = TinyAsciiStr<N>;
47
48    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
49        write!(formatter, "a TinyAsciiStr<{N}>")
50    }
51
52    #[inline]
53    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
54    where
55        A: SeqAccess<'de>,
56    {
57        let mut bytes = [0u8; N];
58        let mut zeroes = false;
59        for out in &mut bytes.iter_mut().take(N) {
60            let byte = seq
61                .next_element()?
62                .ok_or_else(|| Error::invalid_length(N, &self))?;
63            if byte == 0 {
64                zeroes = true;
65            } else if zeroes {
66                return Err(Error::custom("TinyAsciiStr cannot contain null bytes"));
67            }
68
69            if byte >= 0x80 {
70                return Err(Error::custom("TinyAsciiStr cannot contain non-ascii bytes"));
71            }
72            *out = byte;
73        }
74
75        Ok(unsafe { TinyAsciiStr::from_utf8_unchecked(bytes) })
76    }
77}
78
79impl<'de, const N: usize> Deserialize<'de> for TinyAsciiStr<N> {
80    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
81    where
82        D: Deserializer<'de>,
83    {
84        if deserializer.is_human_readable() {
85            let x: Cow<'de, str> = Deserialize::deserialize(deserializer)?;
86            TinyAsciiStr::try_from_str(&x).map_err(|e| Error::custom(e.to_string()))
87        } else {
88            deserializer.deserialize_tuple(N, TinyAsciiStrVisitor::<N>::new())
89        }
90    }
91}