icu_pattern/frontend/
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 super::*;
6use alloc::borrow::Cow;
7use alloc::vec::Vec;
8
9use ::serde::{Deserialize, Deserializer, Serialize, Serializer};
10
11#[doc(hidden)]
12pub fn deserialize_option_borrowed_cow<'de, 'data, B, D: Deserializer<'de>>(
13    deserializer: D,
14) -> Result<Option<Cow<'data, Pattern<B>>>, D::Error>
15where
16    'de: 'data,
17    B: PatternBackend<Store = str>,
18    B::PlaceholderKeyCow<'data>: Deserialize<'de>,
19    &'data B::Store: Deserialize<'de>,
20{
21    #[derive(Deserialize)]
22    #[serde(transparent)]
23    // Cows fail to borrow in some situations (array, option), but structs of Cows don't.
24    struct CowPatternWrap<'data1, B: PatternBackend<Store = str>>
25    where
26        Box<B::Store>: for<'a> From<&'a B::Store>,
27    {
28        #[serde(
29            borrow,
30            deserialize_with = "deserialize_borrowed_cow::<B, _>",
31            bound = "B::PlaceholderKeyCow<'data1>: Deserialize<'de>, &'data1 B::Store: Deserialize<'de>"
32        )]
33        pub cow: Cow<'data1, Pattern<B>>,
34    }
35
36    Option::<CowPatternWrap<'data, B>>::deserialize(deserializer)
37        .map(|option| option.map(|wrap| wrap.cow))
38}
39
40type HumanReadablePattern<'a, B> =
41    Vec<PatternItemCow<'a, <B as PatternBackend>::PlaceholderKeyCow<'a>>>;
42
43#[derive(Debug, PartialEq)]
44#[allow(clippy::exhaustive_structs)] // newtype
45pub struct PatternString<B: PatternBackend>(pub Box<Pattern<B>>);
46
47impl<B: PatternBackend> Clone for PatternString<B>
48where
49    Box<B::Store>: for<'a> From<&'a B::Store>,
50{
51    fn clone(&self) -> Self {
52        Self(self.0.clone())
53    }
54}
55
56impl<B: PatternBackend> core::ops::Deref for PatternString<B> {
57    type Target = Pattern<B>;
58
59    fn deref(&self) -> &Self::Target {
60        &self.0
61    }
62}
63
64impl<B: PatternBackend> Default for PatternString<B>
65where
66    Box<B::Store>: for<'a> From<&'a B::Store>,
67{
68    fn default() -> Self {
69        Self(Box::<Pattern<B>>::default())
70    }
71}
72
73#[cfg(feature = "serde")]
74impl<'de, B: PatternBackend> serde::Deserialize<'de> for PatternString<B>
75where
76    B::PlaceholderKeyCow<'de>: core::str::FromStr,
77    <B::PlaceholderKeyCow<'de> as core::str::FromStr>::Err: core::fmt::Debug,
78{
79    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
80    where
81        D: serde::Deserializer<'de>,
82    {
83        let pattern_str = String::deserialize(deserializer)?;
84        let pattern = Pattern::<B>::try_from_str(&pattern_str, Default::default())
85            .map_err(<D::Error as ::serde::de::Error>::custom)?;
86        Ok(Self(pattern))
87    }
88}
89
90#[doc(hidden)]
91pub fn deserialize_borrowed_cow<'de, 'data, B, D: Deserializer<'de>>(
92    deserializer: D,
93) -> Result<Cow<'data, Pattern<B>>, D::Error>
94where
95    'de: 'data,
96    B: PatternBackend<Store = str>,
97    B::PlaceholderKeyCow<'data>: Deserialize<'de>,
98    &'data B::Store: Deserialize<'de>,
99{
100    if deserializer.is_human_readable() {
101        Box::<Pattern<B>>::deserialize(deserializer).map(Cow::Owned)
102    } else {
103        <&Pattern<B>>::deserialize(deserializer).map(Cow::Borrowed)
104    }
105}
106
107impl<'de, 'data, B> Deserialize<'de> for Box<Pattern<B>>
108where
109    'de: 'data,
110    B: PatternBackend<Store = str>,
111    B::PlaceholderKeyCow<'data>: Deserialize<'de>,
112{
113    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
114    where
115        D: Deserializer<'de>,
116    {
117        if deserializer.is_human_readable() {
118            Pattern::<B>::try_from_items(
119                <HumanReadablePattern<B>>::deserialize(deserializer)?.into_iter(),
120            )
121        } else {
122            let store = Box::<B::Store>::deserialize(deserializer)?;
123            B::validate_store(&store).map(|()| Pattern::<B>::from_boxed_store_unchecked(store))
124        }
125        .map_err(<D::Error as ::serde::de::Error>::custom)
126    }
127}
128
129impl<'de, 'data, B: PatternBackend> Deserialize<'de> for &'data Pattern<B>
130where
131    'de: 'data,
132    &'data B::Store: Deserialize<'de>,
133{
134    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
135    where
136        D: Deserializer<'de>,
137    {
138        if deserializer.is_human_readable() {
139            Err(<D::Error as ::serde::de::Error>::custom(
140                "human readable format cannot be borrowed",
141            ))
142        } else {
143            let store = <&B::Store>::deserialize(deserializer)?;
144            B::validate_store(store).map_err(<D::Error as ::serde::de::Error>::custom)?;
145            Ok(Pattern::from_ref_store_unchecked(store))
146        }
147    }
148}
149
150impl<B: PatternBackend> Serialize for Pattern<B>
151where
152    B::Store: Serialize,
153    for<'a> B::PlaceholderKeyCow<'a>: Serialize + From<B::PlaceholderKey<'a>>,
154{
155    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
156    where
157        S: Serializer,
158    {
159        if serializer.is_human_readable() {
160            B::iter_items(&self.store)
161                .map(|x| x.into())
162                .collect::<HumanReadablePattern<B>>()
163                .serialize(serializer)
164        } else {
165            self.store.serialize(serializer)
166        }
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173    use crate::SinglePlaceholder;
174    use crate::SinglePlaceholderPattern;
175    use alloc::borrow::Cow;
176
177    #[test]
178    fn test_json() {
179        let pattern_owned =
180            SinglePlaceholderPattern::try_from_str("Hello, {0}!", Default::default()).unwrap();
181        let pattern_cow: Cow<SinglePlaceholderPattern> = Cow::Owned(pattern_owned);
182        let pattern_json = serde_json::to_string(&pattern_cow).unwrap();
183        assert_eq!(
184            pattern_json,
185            r#"[{"Literal":"Hello, "},{"Placeholder":"Singleton"},{"Literal":"!"}]"#
186        );
187        let pattern_deserialized: Cow<SinglePlaceholderPattern> =
188            deserialize_borrowed_cow::<SinglePlaceholder, _>(
189                &mut serde_json::Deserializer::from_str(&pattern_json),
190            )
191            .unwrap();
192        assert_eq!(pattern_cow, pattern_deserialized);
193        assert!(matches!(pattern_deserialized, Cow::Owned(_)));
194    }
195
196    #[test]
197    fn test_postcard() {
198        let pattern_owned =
199            SinglePlaceholderPattern::try_from_str("Hello, {0}!", Default::default()).unwrap();
200        let pattern_cow: Cow<SinglePlaceholderPattern> = Cow::Owned(pattern_owned);
201        let pattern_postcard = postcard::to_stdvec(&pattern_cow).unwrap();
202        assert_eq!(pattern_postcard, b"\x09\x08Hello, !");
203        let pattern_deserialized = deserialize_borrowed_cow::<SinglePlaceholder, _>(
204            &mut postcard::Deserializer::from_bytes(&pattern_postcard),
205        )
206        .unwrap();
207        assert_eq!(pattern_cow, pattern_deserialized);
208        assert!(matches!(pattern_deserialized, Cow::Borrowed(_)));
209    }
210
211    #[test]
212    fn test_rmp() {
213        let pattern_owned =
214            SinglePlaceholderPattern::try_from_str("Hello, {0}!", Default::default()).unwrap();
215        let pattern_cow: Cow<SinglePlaceholderPattern> = Cow::Owned(pattern_owned);
216        let pattern_rmp = rmp_serde::to_vec(&pattern_cow).unwrap();
217        assert_eq!(pattern_rmp, b"\xA9\x08Hello, !");
218        let pattern_deserialized = deserialize_borrowed_cow::<SinglePlaceholder, _>(
219            &mut rmp_serde::Deserializer::from_read_ref(&pattern_rmp),
220        )
221        .unwrap();
222        assert_eq!(pattern_cow, pattern_deserialized);
223        assert!(matches!(pattern_deserialized, Cow::Borrowed(_)));
224    }
225}