/rust/registry/src/index.crates.io-1949cf8c6b5b557f/icu_provider-1.5.0/src/request.rs
Line | Count | Source |
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 | | |
5 | | use crate::{DataError, DataErrorKind}; |
6 | | use core::cmp::Ordering; |
7 | | use core::default::Default; |
8 | | use core::fmt; |
9 | | use core::fmt::Debug; |
10 | | use core::hash::Hash; |
11 | | use core::str::FromStr; |
12 | | use icu_locid::extensions::unicode as unicode_ext; |
13 | | use icu_locid::subtags::{Language, Region, Script, Variants}; |
14 | | use icu_locid::{LanguageIdentifier, Locale}; |
15 | | use writeable::{LengthHint, Writeable}; |
16 | | |
17 | | #[cfg(feature = "experimental")] |
18 | | use alloc::string::String; |
19 | | #[cfg(feature = "experimental")] |
20 | | use core::ops::Deref; |
21 | | #[cfg(feature = "experimental")] |
22 | | use icu_locid::extensions::private::Subtag; |
23 | | #[cfg(feature = "experimental")] |
24 | | use tinystr::TinyAsciiStr; |
25 | | |
26 | | #[cfg(doc)] |
27 | | use icu_locid::subtags::Variant; |
28 | | |
29 | | /// The request type passed into all data provider implementations. |
30 | | #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] |
31 | | #[allow(clippy::exhaustive_structs)] // this type is stable |
32 | | pub struct DataRequest<'a> { |
33 | | /// The locale for which to load data. |
34 | | /// |
35 | | /// If locale fallback is enabled, the resulting data may be from a different locale |
36 | | /// than the one requested here. |
37 | | pub locale: &'a DataLocale, |
38 | | /// Metadata that may affect the behavior of the data provider. |
39 | | pub metadata: DataRequestMetadata, |
40 | | } |
41 | | |
42 | | impl fmt::Display for DataRequest<'_> { |
43 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
44 | 0 | fmt::Display::fmt(&self.locale, f) |
45 | 0 | } |
46 | | } |
47 | | |
48 | | /// Metadata for data requests. This is currently empty, but it may be extended with options |
49 | | /// for tuning locale fallback, buffer layout, and so forth. |
50 | | #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
51 | | #[non_exhaustive] |
52 | | pub struct DataRequestMetadata { |
53 | | /// Silent requests do not log errors. This can be used for exploratory querying, such as fallbacks. |
54 | | pub silent: bool, |
55 | | } |
56 | | |
57 | | /// A locale type optimized for use in fallbacking and the ICU4X data pipeline. |
58 | | /// |
59 | | /// [`DataLocale`] contains less functionality than [`Locale`] but more than |
60 | | /// [`LanguageIdentifier`] for better size and performance while still meeting |
61 | | /// the needs of the ICU4X data pipeline. |
62 | | /// |
63 | | /// # Examples |
64 | | /// |
65 | | /// Convert a [`Locale`] to a [`DataLocale`] and back: |
66 | | /// |
67 | | /// ``` |
68 | | /// use icu_locid::locale; |
69 | | /// use icu_provider::DataLocale; |
70 | | /// |
71 | | /// let locale = locale!("en-u-ca-buddhist"); |
72 | | /// let data_locale = DataLocale::from(locale); |
73 | | /// let locale = data_locale.into_locale(); |
74 | | /// |
75 | | /// assert_eq!(locale, locale!("en-u-ca-buddhist")); |
76 | | /// ``` |
77 | | /// |
78 | | /// You can alternatively create a [`DataLocale`] from a borrowed [`Locale`], which is more |
79 | | /// efficient than cloning the [`Locale`], but less efficient than converting an owned |
80 | | /// [`Locale`]: |
81 | | /// |
82 | | /// ``` |
83 | | /// use icu_locid::locale; |
84 | | /// use icu_provider::DataLocale; |
85 | | /// |
86 | | /// let locale1 = locale!("en-u-ca-buddhist"); |
87 | | /// let data_locale = DataLocale::from(&locale1); |
88 | | /// let locale2 = data_locale.into_locale(); |
89 | | /// |
90 | | /// assert_eq!(locale1, locale2); |
91 | | /// ``` |
92 | | /// |
93 | | /// If you are sure that you have no Unicode keywords, start with [`LanguageIdentifier`]: |
94 | | /// |
95 | | /// ``` |
96 | | /// use icu_locid::langid; |
97 | | /// use icu_provider::DataLocale; |
98 | | /// |
99 | | /// let langid = langid!("es-CA-valencia"); |
100 | | /// let data_locale = DataLocale::from(langid); |
101 | | /// let langid = data_locale.get_langid(); |
102 | | /// |
103 | | /// assert_eq!(langid, langid!("es-CA-valencia")); |
104 | | /// ``` |
105 | | /// |
106 | | /// [`DataLocale`] only supports `-u` keywords, to reflect the current state of CLDR data |
107 | | /// lookup and fallback. This may change in the future. |
108 | | /// |
109 | | /// ``` |
110 | | /// use icu_locid::{locale, Locale}; |
111 | | /// use icu_provider::DataLocale; |
112 | | /// |
113 | | /// let locale = "hi-t-en-h0-hybrid-u-attr-ca-buddhist" |
114 | | /// .parse::<Locale>() |
115 | | /// .unwrap(); |
116 | | /// let data_locale = DataLocale::from(locale); |
117 | | /// |
118 | | /// assert_eq!(data_locale.into_locale(), locale!("hi-u-ca-buddhist")); |
119 | | /// ``` |
120 | | #[derive(PartialEq, Clone, Default, Eq, Hash)] |
121 | | pub struct DataLocale { |
122 | | langid: LanguageIdentifier, |
123 | | keywords: unicode_ext::Keywords, |
124 | | #[cfg(feature = "experimental")] |
125 | | aux: Option<AuxiliaryKeys>, |
126 | | } |
127 | | |
128 | | impl<'a> Default for &'a DataLocale { |
129 | 0 | fn default() -> Self { |
130 | | static DEFAULT: DataLocale = DataLocale { |
131 | | langid: LanguageIdentifier::UND, |
132 | | keywords: unicode_ext::Keywords::new(), |
133 | | #[cfg(feature = "experimental")] |
134 | | aux: None, |
135 | | }; |
136 | 0 | &DEFAULT |
137 | 0 | } |
138 | | } |
139 | | |
140 | | impl fmt::Debug for DataLocale { |
141 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
142 | 0 | write!(f, "DataLocale{{{self}}}") |
143 | 0 | } |
144 | | } |
145 | | |
146 | | impl Writeable for DataLocale { |
147 | 0 | fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result { |
148 | 0 | self.langid.write_to(sink)?; |
149 | 0 | if !self.keywords.is_empty() { |
150 | 0 | sink.write_str("-u-")?; |
151 | 0 | self.keywords.write_to(sink)?; |
152 | 0 | } |
153 | | #[cfg(feature = "experimental")] |
154 | 0 | if let Some(aux) = self.aux.as_ref() { |
155 | 0 | sink.write_str("-x-")?; |
156 | 0 | aux.write_to(sink)?; |
157 | 0 | } |
158 | 0 | Ok(()) |
159 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale as writeable::Writeable>::write_to::<writeable::cmp::WriteComparator> Unexecuted instantiation: <icu_provider::request::DataLocale as writeable::Writeable>::write_to::<core::fmt::Formatter> Unexecuted instantiation: <icu_provider::request::DataLocale as writeable::Writeable>::write_to::<alloc::string::String> |
160 | | |
161 | 0 | fn writeable_length_hint(&self) -> LengthHint { |
162 | 0 | let mut length_hint = self.langid.writeable_length_hint(); |
163 | 0 | if !self.keywords.is_empty() { |
164 | 0 | length_hint += self.keywords.writeable_length_hint() + 3; |
165 | 0 | } |
166 | | #[cfg(feature = "experimental")] |
167 | 0 | if let Some(aux) = self.aux.as_ref() { |
168 | 0 | length_hint += aux.writeable_length_hint() + 3; |
169 | 0 | } |
170 | 0 | length_hint |
171 | 0 | } |
172 | | |
173 | 0 | fn write_to_string(&self) -> alloc::borrow::Cow<str> { |
174 | | #[cfg_attr(not(feature = "experimental"), allow(unused_mut))] |
175 | 0 | let mut is_only_langid = self.keywords.is_empty(); |
176 | | #[cfg(feature = "experimental")] |
177 | | { |
178 | 0 | is_only_langid = is_only_langid && self.aux.is_none(); |
179 | | } |
180 | 0 | if is_only_langid { |
181 | 0 | return self.langid.write_to_string(); |
182 | 0 | } |
183 | 0 | let mut string = |
184 | 0 | alloc::string::String::with_capacity(self.writeable_length_hint().capacity()); |
185 | 0 | let _ = self.write_to(&mut string); |
186 | 0 | alloc::borrow::Cow::Owned(string) |
187 | 0 | } |
188 | | } |
189 | | |
190 | | writeable::impl_display_with_writeable!(DataLocale); |
191 | | |
192 | | impl From<LanguageIdentifier> for DataLocale { |
193 | 0 | fn from(langid: LanguageIdentifier) -> Self { |
194 | 0 | Self { |
195 | 0 | langid, |
196 | 0 | keywords: unicode_ext::Keywords::new(), |
197 | 0 | #[cfg(feature = "experimental")] |
198 | 0 | aux: None, |
199 | 0 | } |
200 | 0 | } |
201 | | } |
202 | | |
203 | | impl From<Locale> for DataLocale { |
204 | 0 | fn from(locale: Locale) -> Self { |
205 | 0 | Self { |
206 | 0 | langid: locale.id, |
207 | 0 | keywords: locale.extensions.unicode.keywords, |
208 | 0 | #[cfg(feature = "experimental")] |
209 | 0 | aux: AuxiliaryKeys::try_from_iter(locale.extensions.private.iter().copied()).ok(), |
210 | 0 | } |
211 | 0 | } |
212 | | } |
213 | | |
214 | | impl From<&LanguageIdentifier> for DataLocale { |
215 | 0 | fn from(langid: &LanguageIdentifier) -> Self { |
216 | 0 | Self { |
217 | 0 | langid: langid.clone(), |
218 | 0 | keywords: unicode_ext::Keywords::new(), |
219 | 0 | #[cfg(feature = "experimental")] |
220 | 0 | aux: None, |
221 | 0 | } |
222 | 0 | } |
223 | | } |
224 | | |
225 | | impl From<&Locale> for DataLocale { |
226 | 0 | fn from(locale: &Locale) -> Self { |
227 | 0 | Self { |
228 | 0 | langid: locale.id.clone(), |
229 | 0 | keywords: locale.extensions.unicode.keywords.clone(), |
230 | 0 | #[cfg(feature = "experimental")] |
231 | 0 | aux: AuxiliaryKeys::try_from_iter(locale.extensions.private.iter().copied()).ok(), |
232 | 0 | } |
233 | 0 | } |
234 | | } |
235 | | |
236 | | impl FromStr for DataLocale { |
237 | | type Err = DataError; |
238 | 0 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
239 | 0 | let locale = Locale::from_str(s).map_err(|e| { |
240 | 0 | DataErrorKind::KeyLocaleSyntax |
241 | 0 | .into_error() |
242 | 0 | .with_display_context(s) |
243 | 0 | .with_display_context(&e) |
244 | 0 | })?; |
245 | 0 | Ok(DataLocale::from(locale)) |
246 | 0 | } |
247 | | } |
248 | | |
249 | | impl DataLocale { |
250 | | /// Compare this [`DataLocale`] with BCP-47 bytes. |
251 | | /// |
252 | | /// The return value is equivalent to what would happen if you first converted this |
253 | | /// [`DataLocale`] to a BCP-47 string and then performed a byte comparison. |
254 | | /// |
255 | | /// This function is case-sensitive and results in a *total order*, so it is appropriate for |
256 | | /// binary search. The only argument producing [`Ordering::Equal`] is `self.to_string()`. |
257 | | /// |
258 | | /// # Examples |
259 | | /// |
260 | | /// ``` |
261 | | /// use icu_provider::DataLocale; |
262 | | /// use std::cmp::Ordering; |
263 | | /// |
264 | | /// let bcp47_strings: &[&str] = &[ |
265 | | /// "ca", |
266 | | /// "ca-ES", |
267 | | /// "ca-ES-u-ca-buddhist", |
268 | | /// "ca-ES-valencia", |
269 | | /// "ca-ES-x-gbp", |
270 | | /// "ca-ES-x-gbp-short", |
271 | | /// "ca-ES-x-usd", |
272 | | /// "ca-ES-xyzabc", |
273 | | /// "ca-x-eur", |
274 | | /// "cat", |
275 | | /// "pl-Latn-PL", |
276 | | /// "und", |
277 | | /// "und-fonipa", |
278 | | /// "und-u-ca-hebrew", |
279 | | /// "und-u-ca-japanese", |
280 | | /// "und-x-mxn", |
281 | | /// "zh", |
282 | | /// ]; |
283 | | /// |
284 | | /// for ab in bcp47_strings.windows(2) { |
285 | | /// let a = ab[0]; |
286 | | /// let b = ab[1]; |
287 | | /// assert_eq!(a.cmp(b), Ordering::Less, "strings: {} < {}", a, b); |
288 | | /// let a_loc: DataLocale = a.parse().unwrap(); |
289 | | /// assert_eq!( |
290 | | /// a_loc.strict_cmp(a.as_bytes()), |
291 | | /// Ordering::Equal, |
292 | | /// "strict_cmp: {} == {}", |
293 | | /// a_loc, |
294 | | /// a |
295 | | /// ); |
296 | | /// assert_eq!( |
297 | | /// a_loc.strict_cmp(b.as_bytes()), |
298 | | /// Ordering::Less, |
299 | | /// "strict_cmp: {} < {}", |
300 | | /// a_loc, |
301 | | /// b |
302 | | /// ); |
303 | | /// let b_loc: DataLocale = b.parse().unwrap(); |
304 | | /// assert_eq!( |
305 | | /// b_loc.strict_cmp(b.as_bytes()), |
306 | | /// Ordering::Equal, |
307 | | /// "strict_cmp: {} == {}", |
308 | | /// b_loc, |
309 | | /// b |
310 | | /// ); |
311 | | /// assert_eq!( |
312 | | /// b_loc.strict_cmp(a.as_bytes()), |
313 | | /// Ordering::Greater, |
314 | | /// "strict_cmp: {} > {}", |
315 | | /// b_loc, |
316 | | /// a |
317 | | /// ); |
318 | | /// } |
319 | | /// ``` |
320 | | /// |
321 | | /// Comparison against invalid strings: |
322 | | /// |
323 | | /// ``` |
324 | | /// use icu_provider::DataLocale; |
325 | | /// |
326 | | /// let invalid_strings: &[&str] = &[ |
327 | | /// // Less than "ca-ES" |
328 | | /// "CA", |
329 | | /// "ar-x-gbp-FOO", |
330 | | /// // Greater than "ca-ES-x-gbp" |
331 | | /// "ca_ES", |
332 | | /// "ca-ES-x-gbp-FOO", |
333 | | /// ]; |
334 | | /// |
335 | | /// let data_locale = "ca-ES-x-gbp".parse::<DataLocale>().unwrap(); |
336 | | /// |
337 | | /// for s in invalid_strings.iter() { |
338 | | /// let expected_ordering = "ca-ES-x-gbp".cmp(s); |
339 | | /// let actual_ordering = data_locale.strict_cmp(s.as_bytes()); |
340 | | /// assert_eq!(expected_ordering, actual_ordering, "{}", s); |
341 | | /// } |
342 | | /// ``` |
343 | 0 | pub fn strict_cmp(&self, other: &[u8]) -> Ordering { |
344 | 0 | self.writeable_cmp_bytes(other) |
345 | 0 | } |
346 | | } |
347 | | |
348 | | impl DataLocale { |
349 | | /// Returns whether this [`DataLocale`] has all empty fields (no components). |
350 | | /// |
351 | | /// See also: |
352 | | /// |
353 | | /// - [`DataLocale::is_und()`] |
354 | | /// - [`DataLocale::is_langid_und()`] |
355 | | /// |
356 | | /// # Examples |
357 | | /// |
358 | | /// ``` |
359 | | /// use icu_provider::DataLocale; |
360 | | /// |
361 | | /// assert!("und".parse::<DataLocale>().unwrap().is_empty()); |
362 | | /// assert!(!"und-u-ca-buddhist" |
363 | | /// .parse::<DataLocale>() |
364 | | /// .unwrap() |
365 | | /// .is_empty()); |
366 | | /// assert!(!"und-x-aux".parse::<DataLocale>().unwrap().is_empty()); |
367 | | /// assert!(!"ca-ES".parse::<DataLocale>().unwrap().is_empty()); |
368 | | /// ``` |
369 | 0 | pub fn is_empty(&self) -> bool { |
370 | 0 | self == <&DataLocale>::default() |
371 | 0 | } |
372 | | |
373 | | /// Returns an ordering suitable for use in [`BTreeSet`]. |
374 | | /// |
375 | | /// The ordering may or may not be equivalent to string ordering, and it |
376 | | /// may or may not be stable across ICU4X releases. |
377 | | /// |
378 | | /// [`BTreeSet`]: alloc::collections::BTreeSet |
379 | 0 | pub fn total_cmp(&self, other: &Self) -> Ordering { |
380 | 0 | self.langid |
381 | 0 | .total_cmp(&other.langid) |
382 | 0 | .then_with(|| self.keywords.cmp(&other.keywords)) |
383 | 0 | .then_with(|| { |
384 | | #[cfg(feature = "experimental")] |
385 | 0 | return self.aux.cmp(&other.aux); |
386 | | #[cfg(not(feature = "experimental"))] |
387 | | return Ordering::Equal; |
388 | 0 | }) |
389 | 0 | } |
390 | | |
391 | | /// Returns whether this [`DataLocale`] is `und` in the locale and extensions portion. |
392 | | /// |
393 | | /// This ignores auxiliary keys. |
394 | | /// |
395 | | /// See also: |
396 | | /// |
397 | | /// - [`DataLocale::is_empty()`] |
398 | | /// - [`DataLocale::is_langid_und()`] |
399 | | /// |
400 | | /// # Examples |
401 | | /// |
402 | | /// ``` |
403 | | /// use icu_provider::DataLocale; |
404 | | /// |
405 | | /// assert!("und".parse::<DataLocale>().unwrap().is_und()); |
406 | | /// assert!(!"und-u-ca-buddhist".parse::<DataLocale>().unwrap().is_und()); |
407 | | /// assert!("und-x-aux".parse::<DataLocale>().unwrap().is_und()); |
408 | | /// assert!(!"ca-ES".parse::<DataLocale>().unwrap().is_und()); |
409 | | /// ``` |
410 | 0 | pub fn is_und(&self) -> bool { |
411 | 0 | self.langid == LanguageIdentifier::UND && self.keywords.is_empty() |
412 | 0 | } |
413 | | |
414 | | /// Returns whether the [`LanguageIdentifier`] associated with this request is `und`. |
415 | | /// |
416 | | /// This ignores extension keywords and auxiliary keys. |
417 | | /// |
418 | | /// See also: |
419 | | /// |
420 | | /// - [`DataLocale::is_empty()`] |
421 | | /// - [`DataLocale::is_und()`] |
422 | | /// |
423 | | /// # Examples |
424 | | /// |
425 | | /// ``` |
426 | | /// use icu_provider::DataLocale; |
427 | | /// |
428 | | /// assert!("und".parse::<DataLocale>().unwrap().is_langid_und()); |
429 | | /// assert!("und-u-ca-buddhist" |
430 | | /// .parse::<DataLocale>() |
431 | | /// .unwrap() |
432 | | /// .is_langid_und()); |
433 | | /// assert!("und-x-aux".parse::<DataLocale>().unwrap().is_langid_und()); |
434 | | /// assert!(!"ca-ES".parse::<DataLocale>().unwrap().is_langid_und()); |
435 | | /// ``` |
436 | 0 | pub fn is_langid_und(&self) -> bool { |
437 | 0 | self.langid == LanguageIdentifier::UND |
438 | 0 | } |
439 | | |
440 | | /// Gets the [`LanguageIdentifier`] for this [`DataLocale`]. |
441 | | /// |
442 | | /// This may allocate memory if there are variant subtags. If you need only the language, |
443 | | /// script, and/or region subtag, use the specific getters for those subtags: |
444 | | /// |
445 | | /// - [`DataLocale::language()`] |
446 | | /// - [`DataLocale::script()`] |
447 | | /// - [`DataLocale::region()`] |
448 | | /// |
449 | | /// If you have ownership over the `DataLocale`, use [`DataLocale::into_locale()`] |
450 | | /// and then access the `id` field. |
451 | | /// |
452 | | /// # Examples |
453 | | /// |
454 | | /// ``` |
455 | | /// use icu_locid::langid; |
456 | | /// use icu_provider::prelude::*; |
457 | | /// |
458 | | /// const FOO_BAR: DataKey = icu_provider::data_key!("foo/bar@1"); |
459 | | /// |
460 | | /// let req_no_langid = DataRequest { |
461 | | /// locale: &Default::default(), |
462 | | /// metadata: Default::default(), |
463 | | /// }; |
464 | | /// |
465 | | /// let req_with_langid = DataRequest { |
466 | | /// locale: &langid!("ar-EG").into(), |
467 | | /// metadata: Default::default(), |
468 | | /// }; |
469 | | /// |
470 | | /// assert_eq!(req_no_langid.locale.get_langid(), langid!("und")); |
471 | | /// assert_eq!(req_with_langid.locale.get_langid(), langid!("ar-EG")); |
472 | | /// ``` |
473 | 0 | pub fn get_langid(&self) -> LanguageIdentifier { |
474 | 0 | self.langid.clone() |
475 | 0 | } |
476 | | |
477 | | /// Overrides the entire [`LanguageIdentifier`] portion of this [`DataLocale`]. |
478 | | #[inline] |
479 | 0 | pub fn set_langid(&mut self, lid: LanguageIdentifier) { |
480 | 0 | self.langid = lid; |
481 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::set_langid Unexecuted instantiation: <icu_provider::request::DataLocale>::set_langid |
482 | | |
483 | | /// Converts this [`DataLocale`] into a [`Locale`]. |
484 | | /// |
485 | | /// See also [`DataLocale::get_langid()`]. |
486 | | /// |
487 | | /// # Examples |
488 | | /// |
489 | | /// ``` |
490 | | /// use icu_locid::{ |
491 | | /// langid, locale, |
492 | | /// subtags::{language, region}, |
493 | | /// }; |
494 | | /// use icu_provider::prelude::*; |
495 | | /// |
496 | | /// let locale: DataLocale = locale!("it-IT-u-ca-coptic").into(); |
497 | | /// |
498 | | /// assert_eq!(locale.get_langid(), langid!("it-IT")); |
499 | | /// assert_eq!(locale.language(), language!("it")); |
500 | | /// assert_eq!(locale.script(), None); |
501 | | /// assert_eq!(locale.region(), Some(region!("IT"))); |
502 | | /// |
503 | | /// let locale = locale.into_locale(); |
504 | | /// assert_eq!(locale, locale!("it-IT-u-ca-coptic")); |
505 | | /// ``` |
506 | | /// |
507 | | /// Auxiliary keys are retained: |
508 | | /// |
509 | | /// ``` |
510 | | /// use icu_provider::prelude::*; |
511 | | /// use writeable::assert_writeable_eq; |
512 | | /// |
513 | | /// let data_locale: DataLocale = "und-u-nu-arab-x-gbp".parse().unwrap(); |
514 | | /// assert_writeable_eq!(data_locale, "und-u-nu-arab-x-gbp"); |
515 | | /// |
516 | | /// let recovered_locale = data_locale.into_locale(); |
517 | | /// assert_writeable_eq!(recovered_locale, "und-u-nu-arab-x-gbp"); |
518 | | /// ``` |
519 | 0 | pub fn into_locale(self) -> Locale { |
520 | 0 | let mut loc = Locale { |
521 | 0 | id: self.langid, |
522 | 0 | ..Default::default() |
523 | 0 | }; |
524 | 0 | loc.extensions.unicode.keywords = self.keywords; |
525 | | #[cfg(feature = "experimental")] |
526 | 0 | if let Some(aux) = self.aux { |
527 | 0 | loc.extensions.private = |
528 | 0 | icu_locid::extensions::private::Private::from_vec_unchecked(aux.iter().collect()); |
529 | 0 | } |
530 | 0 | loc |
531 | 0 | } |
532 | | |
533 | | /// Returns the [`Language`] for this [`DataLocale`]. |
534 | | #[inline] |
535 | 0 | pub fn language(&self) -> Language { |
536 | 0 | self.langid.language |
537 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::language Unexecuted instantiation: <icu_provider::request::DataLocale>::language Unexecuted instantiation: <icu_provider::request::DataLocale>::language |
538 | | |
539 | | /// Returns the [`Language`] for this [`DataLocale`]. |
540 | | #[inline] |
541 | 0 | pub fn set_language(&mut self, language: Language) { |
542 | 0 | self.langid.language = language; |
543 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::set_language Unexecuted instantiation: <icu_provider::request::DataLocale>::set_language |
544 | | |
545 | | /// Returns the [`Script`] for this [`DataLocale`]. |
546 | | #[inline] |
547 | 0 | pub fn script(&self) -> Option<Script> { |
548 | 0 | self.langid.script |
549 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::script Unexecuted instantiation: <icu_provider::request::DataLocale>::script |
550 | | |
551 | | /// Sets the [`Script`] for this [`DataLocale`]. |
552 | | #[inline] |
553 | 0 | pub fn set_script(&mut self, script: Option<Script>) { |
554 | 0 | self.langid.script = script; |
555 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::set_script Unexecuted instantiation: <icu_provider::request::DataLocale>::set_script |
556 | | |
557 | | /// Returns the [`Region`] for this [`DataLocale`]. |
558 | | #[inline] |
559 | 0 | pub fn region(&self) -> Option<Region> { |
560 | 0 | self.langid.region |
561 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::region Unexecuted instantiation: <icu_provider::request::DataLocale>::region |
562 | | |
563 | | /// Sets the [`Region`] for this [`DataLocale`]. |
564 | | #[inline] |
565 | 0 | pub fn set_region(&mut self, region: Option<Region>) { |
566 | 0 | self.langid.region = region; |
567 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::set_region Unexecuted instantiation: <icu_provider::request::DataLocale>::set_region |
568 | | |
569 | | /// Returns whether there are any [`Variant`] subtags in this [`DataLocale`]. |
570 | | #[inline] |
571 | 0 | pub fn has_variants(&self) -> bool { |
572 | 0 | !self.langid.variants.is_empty() |
573 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::has_variants Unexecuted instantiation: <icu_provider::request::DataLocale>::has_variants |
574 | | |
575 | | /// Sets all [`Variants`] on this [`DataLocale`], overwriting any that were there previously. |
576 | | #[inline] |
577 | 0 | pub fn set_variants(&mut self, variants: Variants) { |
578 | 0 | self.langid.variants = variants; |
579 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::set_variants Unexecuted instantiation: <icu_provider::request::DataLocale>::set_variants |
580 | | |
581 | | /// Removes all [`Variant`] subtags in this [`DataLocale`]. |
582 | | #[inline] |
583 | 0 | pub fn clear_variants(&mut self) -> Variants { |
584 | 0 | self.langid.variants.clear() |
585 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::clear_variants Unexecuted instantiation: <icu_provider::request::DataLocale>::clear_variants |
586 | | |
587 | | /// Gets the value of the specified Unicode extension keyword for this [`DataLocale`]. |
588 | | #[inline] |
589 | 0 | pub fn get_unicode_ext(&self, key: &unicode_ext::Key) -> Option<unicode_ext::Value> { |
590 | 0 | self.keywords.get(key).cloned() |
591 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::get_unicode_ext Unexecuted instantiation: <icu_provider::request::DataLocale>::get_unicode_ext Unexecuted instantiation: <icu_provider::request::DataLocale>::get_unicode_ext |
592 | | |
593 | | /// Returns whether there are any Unicode extension keywords in this [`DataLocale`]. |
594 | | #[inline] |
595 | 0 | pub fn has_unicode_ext(&self) -> bool { |
596 | 0 | !self.keywords.is_empty() |
597 | 0 | } |
598 | | |
599 | | /// Returns whether a specific Unicode extension keyword is present in this [`DataLocale`]. |
600 | | #[inline] |
601 | 0 | pub fn contains_unicode_ext(&self, key: &unicode_ext::Key) -> bool { |
602 | 0 | self.keywords.contains_key(key) |
603 | 0 | } |
604 | | |
605 | | /// Returns whether this [`DataLocale`] contains a Unicode extension keyword |
606 | | /// with the specified key and value. |
607 | | /// |
608 | | /// # Examples |
609 | | /// |
610 | | /// ``` |
611 | | /// use icu_locid::extensions::unicode::{key, value}; |
612 | | /// use icu_provider::prelude::*; |
613 | | /// |
614 | | /// let locale: DataLocale = "it-IT-u-ca-coptic".parse().expect("Valid BCP-47"); |
615 | | /// |
616 | | /// assert_eq!(locale.get_unicode_ext(&key!("hc")), None); |
617 | | /// assert_eq!(locale.get_unicode_ext(&key!("ca")), Some(value!("coptic"))); |
618 | | /// assert!(locale.matches_unicode_ext(&key!("ca"), &value!("coptic"),)); |
619 | | /// ``` |
620 | | #[inline] |
621 | 0 | pub fn matches_unicode_ext(&self, key: &unicode_ext::Key, value: &unicode_ext::Value) -> bool { |
622 | 0 | self.keywords.get(key) == Some(value) |
623 | 0 | } |
624 | | |
625 | | /// Sets the value for a specific Unicode extension keyword on this [`DataLocale`]. |
626 | | #[inline] |
627 | 0 | pub fn set_unicode_ext( |
628 | 0 | &mut self, |
629 | 0 | key: unicode_ext::Key, |
630 | 0 | value: unicode_ext::Value, |
631 | 0 | ) -> Option<unicode_ext::Value> { |
632 | 0 | self.keywords.set(key, value) |
633 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::set_unicode_ext Unexecuted instantiation: <icu_provider::request::DataLocale>::set_unicode_ext |
634 | | |
635 | | /// Removes a specific Unicode extension keyword from this [`DataLocale`], returning |
636 | | /// the value if it was present. |
637 | | #[inline] |
638 | 0 | pub fn remove_unicode_ext(&mut self, key: &unicode_ext::Key) -> Option<unicode_ext::Value> { |
639 | 0 | self.keywords.remove(key) |
640 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::remove_unicode_ext Unexecuted instantiation: <icu_provider::request::DataLocale>::remove_unicode_ext |
641 | | |
642 | | /// Retains a subset of keywords as specified by the predicate function. |
643 | | #[inline] |
644 | 0 | pub fn retain_unicode_ext<F>(&mut self, predicate: F) |
645 | 0 | where |
646 | 0 | F: FnMut(&unicode_ext::Key) -> bool, |
647 | | { |
648 | 0 | self.keywords.retain_by_key(predicate) |
649 | 0 | } Unexecuted instantiation: <icu_provider::request::DataLocale>::retain_unicode_ext::<<icu_locid_transform::fallback::LocaleFallbackerWithConfig>::normalize::{closure#0}>Unexecuted instantiation: <icu_provider::request::DataLocale>::retain_unicode_ext::<_> |
650 | | |
651 | | /// Gets the auxiliary key for this [`DataLocale`]. |
652 | | /// |
653 | | /// For more information and examples, see [`AuxiliaryKeys`]. |
654 | | #[cfg(feature = "experimental")] |
655 | 0 | pub fn get_aux(&self) -> Option<&AuxiliaryKeys> { |
656 | 0 | self.aux.as_ref() |
657 | 0 | } |
658 | | |
659 | | /// Returns whether this [`DataLocale`] has an auxiliary key. |
660 | | /// |
661 | | /// For more information and examples, see [`AuxiliaryKeys`]. |
662 | | #[cfg(feature = "experimental")] |
663 | 0 | pub fn has_aux(&self) -> bool { |
664 | 0 | self.aux.is_some() |
665 | 0 | } |
666 | | |
667 | | /// Sets an auxiliary key on this [`DataLocale`]. |
668 | | /// |
669 | | /// Returns the previous auxiliary key if present. |
670 | | /// |
671 | | /// For more information and examples, see [`AuxiliaryKeys`]. |
672 | | #[cfg(feature = "experimental")] |
673 | 0 | pub fn set_aux(&mut self, value: AuxiliaryKeys) -> Option<AuxiliaryKeys> { |
674 | 0 | self.aux.replace(value) |
675 | 0 | } |
676 | | |
677 | | /// Remove an auxiliary key, if present. Returns the removed auxiliary key. |
678 | | /// |
679 | | /// # Examples |
680 | | /// |
681 | | /// ``` |
682 | | /// use icu_locid::langid; |
683 | | /// use icu_provider::prelude::*; |
684 | | /// use writeable::assert_writeable_eq; |
685 | | /// |
686 | | /// let mut data_locale: DataLocale = langid!("ar-EG").into(); |
687 | | /// let aux = "gbp" |
688 | | /// .parse::<AuxiliaryKeys>() |
689 | | /// .expect("contains valid characters"); |
690 | | /// data_locale.set_aux(aux); |
691 | | /// assert_writeable_eq!(data_locale, "ar-EG-x-gbp"); |
692 | | /// |
693 | | /// let maybe_aux = data_locale.remove_aux(); |
694 | | /// assert_writeable_eq!(data_locale, "ar-EG"); |
695 | | /// assert_writeable_eq!(maybe_aux.unwrap(), "gbp"); |
696 | | /// ``` |
697 | | #[cfg(feature = "experimental")] |
698 | 0 | pub fn remove_aux(&mut self) -> Option<AuxiliaryKeys> { |
699 | 0 | self.aux.take() |
700 | 0 | } |
701 | | } |
702 | | |
703 | | /// The "auxiliary key" is an annotation on [`DataLocale`] that can contain an arbitrary |
704 | | /// information that does not fit into the [`LanguageIdentifier`] or [`Keywords`]. |
705 | | /// |
706 | | /// A [`DataLocale`] can have multiple auxiliary keys, represented by this struct. The auxiliary |
707 | | /// keys are stored as private use subtags following `-x-`. |
708 | | /// |
709 | | /// An auxiliary key currently allows 1-8 lowercase alphanumerics. |
710 | | /// |
711 | | /// <div class="stab unstable"> |
712 | | /// 🚧 This code is experimental; it may change at any time, in breaking or non-breaking ways, |
713 | | /// including in SemVer minor releases. It can be enabled with the "experimental" Cargo feature |
714 | | /// of the `icu_provider` crate. Use with caution. |
715 | | /// <a href="https://github.com/unicode-org/icu4x/issues/3632">#3632</a> |
716 | | /// </div> |
717 | | /// |
718 | | /// # Examples |
719 | | /// |
720 | | /// ``` |
721 | | /// use icu_locid::langid; |
722 | | /// use icu_provider::prelude::*; |
723 | | /// use writeable::assert_writeable_eq; |
724 | | /// |
725 | | /// let mut data_locale: DataLocale = langid!("ar-EG").into(); |
726 | | /// assert_writeable_eq!(data_locale, "ar-EG"); |
727 | | /// assert!(!data_locale.has_aux()); |
728 | | /// assert_eq!(data_locale.get_aux(), None); |
729 | | /// |
730 | | /// let aux = "gbp" |
731 | | /// .parse::<AuxiliaryKeys>() |
732 | | /// .expect("contains valid characters"); |
733 | | /// |
734 | | /// data_locale.set_aux(aux); |
735 | | /// assert_writeable_eq!(data_locale, "ar-EG-x-gbp"); |
736 | | /// assert!(data_locale.has_aux()); |
737 | | /// assert_eq!(data_locale.get_aux(), Some(&"gbp".parse().unwrap())); |
738 | | /// ``` |
739 | | /// |
740 | | /// Multiple auxiliary keys are allowed: |
741 | | /// |
742 | | /// ``` |
743 | | /// use icu_provider::prelude::*; |
744 | | /// use writeable::assert_writeable_eq; |
745 | | /// |
746 | | /// let data_locale = "ar-EG-x-gbp-long".parse::<DataLocale>().unwrap(); |
747 | | /// assert_writeable_eq!(data_locale, "ar-EG-x-gbp-long"); |
748 | | /// assert_eq!(data_locale.get_aux().unwrap().iter().count(), 2); |
749 | | /// ``` |
750 | | /// |
751 | | /// Not all strings are valid auxiliary keys. |
752 | | /// The string must be well-formed and case-normalized: |
753 | | /// |
754 | | /// ``` |
755 | | /// use icu_provider::prelude::*; |
756 | | /// |
757 | | /// assert!("abcdefg".parse::<AuxiliaryKeys>().is_ok()); |
758 | | /// assert!("abc-xyz".parse::<AuxiliaryKeys>().is_ok()); |
759 | | /// |
760 | | /// assert!("".parse::<AuxiliaryKeys>().is_err()); |
761 | | /// assert!("!@#$%".parse::<AuxiliaryKeys>().is_err()); |
762 | | /// assert!("abc_xyz".parse::<AuxiliaryKeys>().is_err()); |
763 | | /// assert!("ABC123".parse::<AuxiliaryKeys>().is_err()); |
764 | | /// ``` |
765 | | /// |
766 | | /// [`Keywords`]: unicode_ext::Keywords |
767 | | #[derive(Debug, PartialEq, Clone, Eq, Hash, PartialOrd, Ord)] |
768 | | #[cfg(feature = "experimental")] |
769 | | pub struct AuxiliaryKeys { |
770 | | value: AuxiliaryKeysInner, |
771 | | } |
772 | | |
773 | | #[cfg(feature = "experimental")] |
774 | | #[derive(Clone)] |
775 | | enum AuxiliaryKeysInner { |
776 | | Boxed(alloc::boxed::Box<str>), |
777 | | Stack(TinyAsciiStr<23>), |
778 | | // NOTE: In the future, a `Static` variant could be added to allow `data_locale!("...")` |
779 | | // Static(&'static str), |
780 | | } |
781 | | |
782 | | #[cfg(feature = "experimental")] |
783 | | impl Deref for AuxiliaryKeysInner { |
784 | | type Target = str; |
785 | | #[inline] |
786 | 0 | fn deref(&self) -> &Self::Target { |
787 | 0 | match self { |
788 | 0 | Self::Boxed(s) => s.deref(), |
789 | 0 | Self::Stack(s) => s.as_str(), |
790 | | } |
791 | 0 | } |
792 | | } |
793 | | |
794 | | #[cfg(feature = "experimental")] |
795 | | impl PartialEq for AuxiliaryKeysInner { |
796 | | #[inline] |
797 | 0 | fn eq(&self, other: &Self) -> bool { |
798 | 0 | self.deref() == other.deref() |
799 | 0 | } |
800 | | } |
801 | | |
802 | | #[cfg(feature = "experimental")] |
803 | | impl Eq for AuxiliaryKeysInner {} |
804 | | |
805 | | #[cfg(feature = "experimental")] |
806 | | impl PartialOrd for AuxiliaryKeysInner { |
807 | 0 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
808 | 0 | Some(self.cmp(other)) |
809 | 0 | } |
810 | | } |
811 | | |
812 | | #[cfg(feature = "experimental")] |
813 | | impl Ord for AuxiliaryKeysInner { |
814 | 0 | fn cmp(&self, other: &Self) -> Ordering { |
815 | 0 | self.deref().cmp(other.deref()) |
816 | 0 | } |
817 | | } |
818 | | |
819 | | #[cfg(feature = "experimental")] |
820 | | impl Debug for AuxiliaryKeysInner { |
821 | | #[inline] |
822 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
823 | 0 | self.deref().fmt(f) |
824 | 0 | } |
825 | | } |
826 | | |
827 | | #[cfg(feature = "experimental")] |
828 | | impl Hash for AuxiliaryKeysInner { |
829 | | #[inline] |
830 | 0 | fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
831 | 0 | self.deref().hash(state) |
832 | 0 | } |
833 | | } |
834 | | |
835 | | #[cfg(feature = "experimental")] |
836 | | writeable::impl_display_with_writeable!(AuxiliaryKeys); |
837 | | |
838 | | #[cfg(feature = "experimental")] |
839 | | impl Writeable for AuxiliaryKeys { |
840 | 0 | fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result { |
841 | 0 | self.value.write_to(sink) |
842 | 0 | } Unexecuted instantiation: <icu_provider::request::AuxiliaryKeys as writeable::Writeable>::write_to::<writeable::cmp::WriteComparator> Unexecuted instantiation: <icu_provider::request::AuxiliaryKeys as writeable::Writeable>::write_to::<core::fmt::Formatter> Unexecuted instantiation: <icu_provider::request::AuxiliaryKeys as writeable::Writeable>::write_to::<alloc::string::String> |
843 | 0 | fn writeable_length_hint(&self) -> LengthHint { |
844 | 0 | self.value.writeable_length_hint() |
845 | 0 | } |
846 | 0 | fn write_to_string(&self) -> alloc::borrow::Cow<str> { |
847 | 0 | self.value.write_to_string() |
848 | 0 | } |
849 | | } |
850 | | |
851 | | #[cfg(feature = "experimental")] |
852 | | impl FromStr for AuxiliaryKeys { |
853 | | type Err = DataError; |
854 | | |
855 | 0 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
856 | 0 | if !s.is_empty() |
857 | 0 | && s.split(Self::separator()).all(|b| { |
858 | 0 | if let Ok(subtag) = Subtag::from_str(b) { |
859 | | // Enforces normalization: |
860 | 0 | b == subtag.as_str() |
861 | | } else { |
862 | 0 | false |
863 | | } |
864 | 0 | }) |
865 | | { |
866 | 0 | if s.len() <= 23 { |
867 | | #[allow(clippy::unwrap_used)] // we just checked that the string is ascii |
868 | 0 | Ok(Self { |
869 | 0 | value: AuxiliaryKeysInner::Stack(s.parse().unwrap()), |
870 | 0 | }) |
871 | | } else { |
872 | 0 | Ok(Self { |
873 | 0 | value: AuxiliaryKeysInner::Boxed(s.into()), |
874 | 0 | }) |
875 | | } |
876 | | } else { |
877 | 0 | Err(DataErrorKind::KeyLocaleSyntax |
878 | 0 | .into_error() |
879 | 0 | .with_display_context(s)) |
880 | | } |
881 | 0 | } |
882 | | } |
883 | | |
884 | | #[cfg(feature = "experimental")] |
885 | | impl AuxiliaryKeys { |
886 | | /// Creates an [`AuxiliaryKeys`] from an iterator of individual keys. |
887 | | /// |
888 | | /// # Examples |
889 | | /// |
890 | | /// ``` |
891 | | /// use icu_locid::extensions::private::subtag; |
892 | | /// use icu_provider::prelude::*; |
893 | | /// |
894 | | /// // Single auxiliary key: |
895 | | /// let a = AuxiliaryKeys::try_from_iter([subtag!("abc")]).unwrap(); |
896 | | /// let b = "abc".parse::<AuxiliaryKeys>().unwrap(); |
897 | | /// assert_eq!(a, b); |
898 | | /// |
899 | | /// // Multiple auxiliary keys: |
900 | | /// let a = AuxiliaryKeys::try_from_iter([subtag!("abc"), subtag!("defg")]) |
901 | | /// .unwrap(); |
902 | | /// let b = "abc-defg".parse::<AuxiliaryKeys>().unwrap(); |
903 | | /// assert_eq!(a, b); |
904 | | /// ``` |
905 | | /// |
906 | | /// The iterator can't be empty: |
907 | | /// |
908 | | /// ``` |
909 | | /// use icu_provider::prelude::*; |
910 | | /// |
911 | | /// assert!(AuxiliaryKeys::try_from_iter([]).is_err()); |
912 | | /// ``` |
913 | 0 | pub fn try_from_iter(iter: impl IntoIterator<Item = Subtag>) -> Result<Self, DataError> { |
914 | | // TODO: Avoid the allocation when possible |
915 | 0 | let mut builder = String::new(); |
916 | 0 | for item in iter { |
917 | 0 | if !builder.is_empty() { |
918 | 0 | builder.push(AuxiliaryKeys::separator()); |
919 | 0 | } |
920 | 0 | builder.push_str(item.as_str()) |
921 | | } |
922 | 0 | if builder.is_empty() { |
923 | 0 | return Err(DataErrorKind::KeyLocaleSyntax.with_str_context("empty aux iterator")); |
924 | 0 | } |
925 | 0 | if builder.len() <= 23 { |
926 | | #[allow(clippy::unwrap_used)] // we just checked that the string is ascii |
927 | 0 | Ok(Self { |
928 | 0 | value: AuxiliaryKeysInner::Stack(builder.parse().unwrap()), |
929 | 0 | }) |
930 | | } else { |
931 | 0 | Ok(Self { |
932 | 0 | value: AuxiliaryKeysInner::Boxed(builder.into()), |
933 | 0 | }) |
934 | | } |
935 | 0 | } |
936 | | |
937 | | /// Creates an [`AuxiliaryKeys`] from a single subtag. |
938 | | /// |
939 | | /// # Examples |
940 | | /// |
941 | | /// ``` |
942 | | /// use icu_locid::extensions::private::subtag; |
943 | | /// use icu_provider::prelude::*; |
944 | | /// |
945 | | /// // Single auxiliary key: |
946 | | /// let a = AuxiliaryKeys::from_subtag(subtag!("abc")); |
947 | | /// let b = "abc".parse::<AuxiliaryKeys>().unwrap(); |
948 | | /// assert_eq!(a, b); |
949 | | /// ``` |
950 | 0 | pub const fn from_subtag(input: Subtag) -> Self { |
951 | 0 | Self { |
952 | 0 | value: AuxiliaryKeysInner::Stack(input.into_tinystr().resize()), |
953 | 0 | } |
954 | 0 | } |
955 | | |
956 | | /// Iterates over the components of the auxiliary key. |
957 | | /// |
958 | | /// # Example |
959 | | /// |
960 | | /// ``` |
961 | | /// use icu_locid::extensions::private::subtag; |
962 | | /// use icu_provider::AuxiliaryKeys; |
963 | | /// |
964 | | /// let aux: AuxiliaryKeys = "abc-defg".parse().unwrap(); |
965 | | /// assert_eq!( |
966 | | /// aux.iter().collect::<Vec<_>>(), |
967 | | /// vec![subtag!("abc"), subtag!("defg")] |
968 | | /// ); |
969 | | /// ``` |
970 | 0 | pub fn iter(&self) -> impl Iterator<Item = Subtag> + '_ { |
971 | 0 | self.value |
972 | 0 | .split(Self::separator()) |
973 | 0 | .filter_map(|x| match x.parse() { |
974 | 0 | Ok(x) => Some(x), |
975 | | Err(_) => { |
976 | 0 | debug_assert!(false, "failed to convert to subtag: {x}"); |
977 | 0 | None |
978 | | } |
979 | 0 | }) |
980 | 0 | } |
981 | | |
982 | | /// Returns the internal separator byte used for auxiliary keys in data locales. |
983 | | /// |
984 | | /// This is, according to BCP-47, an ASCII hyphen. |
985 | | #[inline] |
986 | 0 | pub(crate) const fn separator() -> char { |
987 | 0 | '-' |
988 | 0 | } |
989 | | } |
990 | | |
991 | | #[cfg(feature = "experimental")] |
992 | | impl From<Subtag> for AuxiliaryKeys { |
993 | 0 | fn from(subtag: Subtag) -> Self { |
994 | | #[allow(clippy::expect_used)] // subtags definitely fit within auxiliary keys |
995 | 0 | Self { |
996 | 0 | value: AuxiliaryKeysInner::Stack( |
997 | 0 | TinyAsciiStr::from_bytes(subtag.as_str().as_bytes()) |
998 | 0 | .expect("Subtags are capped to 8 elements, AuxiliaryKeys supports up to 23"), |
999 | 0 | ), |
1000 | 0 | } |
1001 | 0 | } |
1002 | | } |
1003 | | |
1004 | | #[test] |
1005 | | fn test_data_locale_to_string() { |
1006 | | struct TestCase { |
1007 | | pub locale: &'static str, |
1008 | | pub aux: Option<&'static str>, |
1009 | | pub expected: &'static str, |
1010 | | } |
1011 | | |
1012 | | for cas in [ |
1013 | | TestCase { |
1014 | | locale: "und", |
1015 | | aux: None, |
1016 | | expected: "und", |
1017 | | }, |
1018 | | TestCase { |
1019 | | locale: "und-u-cu-gbp", |
1020 | | aux: None, |
1021 | | expected: "und-u-cu-gbp", |
1022 | | }, |
1023 | | TestCase { |
1024 | | locale: "en-ZA-u-cu-gbp", |
1025 | | aux: None, |
1026 | | expected: "en-ZA-u-cu-gbp", |
1027 | | }, |
1028 | | #[cfg(feature = "experimental")] |
1029 | | TestCase { |
1030 | | locale: "en-ZA-u-nu-arab", |
1031 | | aux: Some("gbp"), |
1032 | | expected: "en-ZA-u-nu-arab-x-gbp", |
1033 | | }, |
1034 | | ] { |
1035 | | let mut locale = cas.locale.parse::<DataLocale>().unwrap(); |
1036 | | #[cfg(feature = "experimental")] |
1037 | | if let Some(aux) = cas.aux { |
1038 | | locale.set_aux(aux.parse().unwrap()); |
1039 | | } |
1040 | | writeable::assert_writeable_eq!(locale, cas.expected); |
1041 | | } |
1042 | | } |
1043 | | |
1044 | | #[test] |
1045 | | fn test_data_locale_from_string() { |
1046 | | #[derive(Debug)] |
1047 | | struct TestCase { |
1048 | | pub input: &'static str, |
1049 | | pub success: bool, |
1050 | | } |
1051 | | |
1052 | | for cas in [ |
1053 | | TestCase { |
1054 | | input: "und", |
1055 | | success: true, |
1056 | | }, |
1057 | | TestCase { |
1058 | | input: "und-u-cu-gbp", |
1059 | | success: true, |
1060 | | }, |
1061 | | TestCase { |
1062 | | input: "en-ZA-u-cu-gbp", |
1063 | | success: true, |
1064 | | }, |
1065 | | TestCase { |
1066 | | input: "en...", |
1067 | | success: false, |
1068 | | }, |
1069 | | #[cfg(feature = "experimental")] |
1070 | | TestCase { |
1071 | | input: "en-ZA-u-nu-arab-x-gbp", |
1072 | | success: true, |
1073 | | }, |
1074 | | #[cfg(not(feature = "experimental"))] |
1075 | | TestCase { |
1076 | | input: "en-ZA-u-nu-arab-x-gbp", |
1077 | | success: false, |
1078 | | }, |
1079 | | ] { |
1080 | | let data_locale = match (DataLocale::from_str(cas.input), cas.success) { |
1081 | | (Ok(l), true) => l, |
1082 | | (Err(_), false) => { |
1083 | | continue; |
1084 | | } |
1085 | | (Ok(_), false) => { |
1086 | | panic!("DataLocale parsed but it was supposed to fail: {cas:?}"); |
1087 | | } |
1088 | | (Err(_), true) => { |
1089 | | panic!("DataLocale was supposed to parse but it failed: {cas:?}"); |
1090 | | } |
1091 | | }; |
1092 | | writeable::assert_writeable_eq!(data_locale, cas.input); |
1093 | | } |
1094 | | } |