icu_locale_core/
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::{LanguageIdentifier, Locale};
6use core::{fmt::Display, marker::PhantomData, str::FromStr};
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8use writeable::Writeable;
9
10impl Serialize for LanguageIdentifier {
11    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
12    where
13        S: Serializer,
14    {
15        serializer.serialize_str(&self.write_to_string())
16    }
17}
18
19impl Serialize for Locale {
20    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
21    where
22        S: Serializer,
23    {
24        serializer.serialize_str(&self.write_to_string())
25    }
26}
27
28struct ParseVisitor<T>(PhantomData<T>);
29
30impl<T> serde::de::Visitor<'_> for ParseVisitor<T>
31where
32    T: FromStr,
33    <T as FromStr>::Err: Display,
34{
35    type Value = T;
36
37    fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38        write!(formatter, "a valid Unicode Language or Locale Identifier")
39    }
40
41    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
42    where
43        E: serde::de::Error,
44    {
45        s.parse::<T>().map_err(serde::de::Error::custom)
46    }
47}
48
49impl<'de> Deserialize<'de> for LanguageIdentifier {
50    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
51    where
52        D: Deserializer<'de>,
53    {
54        deserializer.deserialize_str(ParseVisitor(PhantomData))
55    }
56}
57
58impl<'de> Deserialize<'de> for Locale {
59    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60    where
61        D: Deserializer<'de>,
62    {
63        deserializer.deserialize_str(ParseVisitor(PhantomData))
64    }
65}
66
67#[test]
68fn json() {
69    use crate::subtags::{Language, Region, Script};
70    use crate::{langid, locale};
71
72    assert_eq!(
73        serde_json::to_string(&langid!("en-US")).unwrap(),
74        r#""en-US""#
75    );
76    assert_eq!(
77        serde_json::from_str::<LanguageIdentifier>(r#""en-US""#).unwrap(),
78        langid!("en-US")
79    );
80    assert_eq!(
81        serde_json::from_reader::<_, LanguageIdentifier>(&br#""en-US""#[..]).unwrap(),
82        langid!("en-US")
83    );
84    assert!(serde_json::from_str::<LanguageIdentifier>(r#""2Xs""#).is_err());
85
86    assert_eq!(
87        serde_json::to_string(&locale!("en-US-u-hc-h12")).unwrap(),
88        r#""en-US-u-hc-h12""#
89    );
90    assert_eq!(
91        serde_json::from_str::<Locale>(r#""en-US-u-hc-h12""#).unwrap(),
92        locale!("en-US-u-hc-h12")
93    );
94    assert_eq!(
95        serde_json::from_reader::<_, Locale>(&br#""en-US-u-hc-h12""#[..]).unwrap(),
96        locale!("en-US-u-hc-h12")
97    );
98    assert!(serde_json::from_str::<Locale>(r#""2Xs""#).is_err());
99
100    assert_eq!(
101        serde_json::to_string(&"fr".parse::<Language>().unwrap()).unwrap(),
102        r#""fr""#
103    );
104    assert_eq!(
105        serde_json::from_str::<Language>(r#""fr""#).unwrap(),
106        "fr".parse::<Language>().unwrap()
107    );
108    assert_eq!(
109        serde_json::from_reader::<_, Language>(&br#""fr""#[..]).unwrap(),
110        "fr".parse::<Language>().unwrap()
111    );
112    assert!(serde_json::from_str::<Language>(r#""2Xs""#).is_err());
113
114    assert_eq!(
115        serde_json::to_string(&"Latn".parse::<Script>().unwrap()).unwrap(),
116        r#""Latn""#
117    );
118    assert_eq!(
119        serde_json::from_str::<Script>(r#""Latn""#).unwrap(),
120        "Latn".parse::<Script>().unwrap()
121    );
122    assert_eq!(
123        serde_json::from_reader::<_, Script>(&br#""Latn""#[..]).unwrap(),
124        "Latn".parse::<Script>().unwrap()
125    );
126    assert!(serde_json::from_str::<Script>(r#""2Xs""#).is_err());
127
128    assert_eq!(
129        serde_json::to_string(&"US".parse::<Region>().unwrap()).unwrap(),
130        r#""US""#
131    );
132    assert_eq!(
133        serde_json::from_str::<Region>(r#""US""#).unwrap(),
134        "US".parse::<Region>().unwrap()
135    );
136    assert_eq!(
137        serde_json::from_reader::<_, Region>(&br#""US""#[..]).unwrap(),
138        "US".parse::<Region>().unwrap()
139    );
140    assert!(serde_json::from_str::<Region>(r#""2Xs""#).is_err());
141}
142
143#[test]
144fn postcard() {
145    use crate::subtags::{Language, Region, Script};
146    use crate::{langid, locale};
147
148    assert_eq!(
149        postcard::to_stdvec(&langid!("en-US")).unwrap(),
150        b"\x05en-US"
151    );
152    assert_eq!(
153        postcard::from_bytes::<LanguageIdentifier>(b"\x05en-US").unwrap(),
154        langid!("en-US")
155    );
156    assert!(postcard::from_bytes::<LanguageIdentifier>(b"\x032Xs").is_err());
157
158    assert_eq!(
159        postcard::to_stdvec(&locale!("en-US-u-hc-h12")).unwrap(),
160        b"\x0Een-US-u-hc-h12"
161    );
162    assert_eq!(
163        postcard::from_bytes::<Locale>(b"\x0Een-US-u-hc-h12").unwrap(),
164        locale!("en-US-u-hc-h12")
165    );
166    assert!(postcard::from_bytes::<Locale>(b"\x032Xs").is_err());
167
168    assert_eq!(
169        postcard::to_stdvec(&"fr".parse::<Language>().unwrap()).unwrap(),
170        b"fr\0"
171    );
172    assert_eq!(
173        postcard::from_bytes::<Language>(b"fr\0").unwrap(),
174        "fr".parse::<Language>().unwrap()
175    );
176    assert!(postcard::from_bytes::<Language>(b"2Xs").is_err());
177
178    assert_eq!(
179        postcard::to_stdvec(&"Latn".parse::<Script>().unwrap()).unwrap(),
180        b"Latn"
181    );
182    assert_eq!(
183        postcard::from_bytes::<Script>(b"Latn").unwrap(),
184        "Latn".parse::<Script>().unwrap()
185    );
186    assert!(postcard::from_bytes::<Script>(b"2Xss").is_err());
187
188    assert_eq!(
189        postcard::to_stdvec(&"US".parse::<Region>().unwrap()).unwrap(),
190        b"US\0"
191    );
192    assert_eq!(
193        postcard::from_bytes::<Region>(b"US\0").unwrap(),
194        "US".parse::<Region>().unwrap()
195    );
196    assert!(postcard::from_bytes::<Region>(b"2Xs").is_err());
197}