1use std::collections::HashMap;
8use std::fmt;
9use std::fmt::Display;
10use std::str::FromStr;
11
12use locale_codes::{codeset, country, language};
13use locale_types::string::ParseError;
14use locale_types::{LocaleError, LocaleIdentifier, LocaleResult, LocaleString};
15
16#[derive(Debug, PartialEq)]
22pub struct StrictLocaleString(LocaleString);
23
24impl LocaleIdentifier for StrictLocaleString {
29 fn new(language_code: String) -> LocaleResult<Self> {
30 match language::lookup(&language_code) {
31 None => Err(LocaleError::InvalidLanguageCode),
32 Some(_) => Ok(StrictLocaleString(LocaleString::new(language_code)?)),
33 }
34 }
35
36 fn with_language(&self, language_code: String) -> LocaleResult<Self> {
37 match language::lookup(&language_code) {
38 None => Err(LocaleError::InvalidLanguageCode),
39 Some(_) => Ok(StrictLocaleString(self.0.with_language(language_code)?)),
40 }
41 }
42
43 fn with_territory(&self, territory: String) -> LocaleResult<Self> {
44 match country::lookup(&territory) {
45 None => Err(LocaleError::InvalidTerritoryCode),
46 Some(_) => Ok(StrictLocaleString(self.0.with_territory(territory)?)),
47 }
48 }
49
50 fn with_code_set(&self, code_set: String) -> LocaleResult<Self> {
51 match codeset::lookup(&code_set) {
52 None => Err(LocaleError::InvalidCodeSet),
53 Some(_) => Ok(StrictLocaleString(self.0.with_code_set(code_set)?)),
54 }
55 }
56
57 fn with_modifier(&self, modifier: String) -> LocaleResult<Self> {
58 Ok(StrictLocaleString(self.0.with_modifier(modifier)?))
59 }
60
61 fn with_modifiers<K, V>(&self, modifiers: HashMap<K, V>) -> LocaleResult<Self>
62 where
63 K: Display,
64 V: Display,
65 {
66 Ok(StrictLocaleString(self.0.with_modifiers(modifiers)?))
67 }
68
69 fn language_code(&self) -> String {
70 self.0.language_code()
71 }
72
73 fn territory(&self) -> Option<String> {
74 self.0.territory()
75 }
76
77 fn code_set(&self) -> Option<String> {
78 self.0.code_set()
79 }
80
81 fn modifier(&self) -> Option<String> {
82 self.0.modifier()
83 }
84}
85
86impl Display for StrictLocaleString {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 write!(f, "{}", self.0)
89 }
90}
91
92impl FromStr for StrictLocaleString {
93 type Err = ParseError;
94
95 fn from_str(s: &str) -> Result<Self, Self::Err> {
96 match LocaleString::from_str(s) {
97 Err(e) => Err(e),
98 Ok(locale) => {
99 let mut strict = StrictLocaleString::new(locale.language_code()).unwrap();
100 if let Some(territory) = locale.territory() {
101 strict = strict.with_territory(territory).unwrap();
102 }
103 if let Some(code_set) = locale.code_set() {
104 strict = strict.with_code_set(code_set).unwrap();
105 }
106 if let Some(modifier) = locale.modifier() {
107 strict = strict.with_modifier(modifier).unwrap();
108 }
109 Ok(strict)
110 }
111 }
112 }
113}
114
115#[cfg(test)]
120mod tests {
121 use std::str::FromStr;
122
123 use crate::StrictLocaleString;
124 use locale_types::{LocaleError, LocaleIdentifier};
125
126 #[test]
128 fn test_unknown_language() {
129 assert_eq!(
130 StrictLocaleString::new("xx".to_string()),
131 Err(LocaleError::InvalidLanguageCode)
132 );
133 }
134
135 #[test]
136 fn test_unknown_territory() {
137 assert_eq!(
138 StrictLocaleString::new("en".to_string())
139 .unwrap()
140 .with_territory("XX".to_string()),
141 Err(LocaleError::InvalidTerritoryCode)
142 );
143 }
144
145 #[test]
146 fn test_unknown_code_set() {
147 assert_eq!(
148 StrictLocaleString::new("en".to_string())
149 .unwrap()
150 .with_code_set("UNKNOWN".to_string()),
151 Err(LocaleError::InvalidCodeSet)
152 );
153 }
154
155 #[test]
157 fn test_constructor() {
158 let locale = StrictLocaleString::new("en".to_string()).unwrap();
159 assert_eq!(locale.language_code(), "en".to_string());
160 assert_eq!(locale.territory(), None);
161 assert_eq!(locale.modifier(), None);
162 }
163
164 #[test]
165 fn test_with_language() {
166 let locale = StrictLocaleString::new("en".to_string()).unwrap();
167 assert_eq!(
168 locale
169 .with_language("fr".to_string())
170 .unwrap()
171 .language_code(),
172 "fr".to_string()
173 );
174 }
175
176 #[test]
177 fn test_with_territory() {
178 let locale = StrictLocaleString::new("en".to_string()).unwrap();
179 assert_eq!(
180 locale.with_territory("GB".to_string()).unwrap().territory(),
181 Some("GB".to_string())
182 );
183 }
184
185 #[test]
186 fn test_with_code_set() {
187 let locale = StrictLocaleString::new("en".to_string()).unwrap();
188 assert_eq!(
189 locale
190 .with_code_set("UTF-8".to_string())
191 .unwrap()
192 .code_set(),
193 Some("UTF-8".to_string())
194 );
195 }
196
197 #[test]
199 fn test_to_string() {
200 let locale = StrictLocaleString::new("en".to_string())
201 .unwrap()
202 .with_territory("US".to_string())
203 .unwrap()
204 .with_code_set("UTF-8".to_string())
205 .unwrap()
206 .with_modifier("collation=pinyin;currency=CNY".to_string())
207 .unwrap();
208 assert_eq!(
209 locale.to_string(),
210 "en_US.UTF-8@collation=pinyin;currency=CNY".to_string()
211 );
212 }
213
214 #[test]
216 fn test_from_str_1() {
217 match StrictLocaleString::from_str("en") {
218 Ok(locale) => assert_eq!(locale.language_code(), "en"),
219 _ => panic!("LocaleString::from_str failure"),
220 }
221 }
222
223 #[test]
224 fn test_from_str_2() {
225 match StrictLocaleString::from_str("en_US") {
226 Ok(locale) => {
227 assert_eq!(locale.language_code(), "en");
228 assert_eq!(locale.territory(), Some("US".to_string()));
229 }
230 _ => panic!("LocaleString::from_str failure"),
231 }
232 }
233
234 #[test]
235 fn test_from_str_3() {
236 match StrictLocaleString::from_str("en_US.UTF-8") {
237 Ok(locale) => {
238 assert_eq!(locale.language_code(), "en");
239 assert_eq!(locale.territory(), Some("US".to_string()));
240 assert_eq!(locale.code_set(), Some("UTF-8".to_string()));
241 }
242 _ => panic!("LocaleString::from_str failure"),
243 }
244 }
245
246 #[test]
247 fn test_from_str_4() {
248 match StrictLocaleString::from_str("en_US.UTF-8@Latn") {
249 Ok(locale) => {
250 assert_eq!(locale.language_code(), "en");
251 assert_eq!(locale.territory(), Some("US".to_string()));
252 assert_eq!(locale.code_set(), Some("UTF-8".to_string()));
253 assert_eq!(locale.modifier(), Some("Latn".to_string()));
254 }
255 _ => panic!("LocaleString::from_str failure"),
256 }
257 }
258}