unicode_locale_parser/
locale.rs

1use crate::constants::SEP;
2use crate::errors::ParserError;
3use crate::extensions::{parse_extensions_from_iter, Extensions};
4use crate::lang::{parse_unicode_language_id_from_iter, UnicodeLanguageIdentifier};
5use crate::shared::split_str;
6
7use std::fmt::{self};
8use std::str;
9use std::str::FromStr;
10
11#[derive(Debug)]
12pub struct UnicodeLocaleIdentifier {
13    pub language: UnicodeLanguageIdentifier,
14    pub extensions: Extensions,
15}
16
17impl fmt::Display for UnicodeLocaleIdentifier {
18    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
19        let mut msg = vec![];
20        msg.push(format!("{}", self.language));
21        let extensions_msg = format!("{}", self.extensions);
22        if !extensions_msg.is_empty() {
23            msg.push(extensions_msg);
24        }
25        f.write_str(&msg.join(&SEP.to_string()))?;
26        Ok(())
27    }
28}
29
30impl FromStr for UnicodeLocaleIdentifier {
31    type Err = ParserError;
32
33    fn from_str(source: &str) -> Result<Self, Self::Err> {
34        parse_unicode_locale_id(source)
35    }
36}
37
38/// Parse the given string as an Unicode Locale Identifier.
39///
40/// This function parses according to [`unicode_locale_id` EBNF defined in UTS #35](https://unicode.org/reports/tr35/#unicode_locale_id)
41///
42/// # Examples
43///
44/// ```
45/// use unicode_locale_parser::parse_locale_id;
46///
47/// let locale = parse_locale_id("en-US-u-hc-h12").unwrap();
48/// assert_eq!("en", locale.language.language);
49/// assert_eq!(None, locale.language.script);
50/// assert_eq!(Some("US".to_string()), locale.language.region);
51/// assert_eq!(None, locale.language.variants);
52/// let u = locale.extensions.unicode_locale.unwrap();
53/// assert_eq!(
54///     &vec!["h12".to_string()],
55///     u.get(0).unwrap().ufield.get("hc").unwrap()
56/// );
57/// ```
58///
59/// # Errors
60///
61/// This function returns an error in the following cases:
62///
63/// - [`ParserError::Missing`] if the given locale id is empty.
64/// - [`ParserError::InvalidLanguage`] if the given locale id is not a valid language identifier.
65/// - [`ParserError::InvalidSubtag`] if the given locale id is not a valid subtag.
66/// - [`ParserError::InvalidExtension`] if the given locale id is not a valid unicode extensions
67pub fn parse_unicode_locale_id(locale_id: &str) -> Result<UnicodeLocaleIdentifier, ParserError> {
68    // check empty
69    if locale_id.is_empty() {
70        return Err(ParserError::Missing);
71    }
72
73    let mut iter = split_str(locale_id).peekable();
74    let language = parse_unicode_language_id_from_iter(&mut iter)?;
75    let extensions = parse_extensions_from_iter(&mut iter)?;
76
77    Ok(UnicodeLocaleIdentifier {
78        language,
79        extensions,
80    })
81}
82
83/*
84 * Unit tests
85 */
86
87#[test]
88fn success_parse_unicode_locale_id() {
89    // basic
90    let locale = parse_unicode_locale_id("en-US-u-hc-h12").unwrap();
91    assert_eq!("en", locale.language.language);
92    assert_eq!(None, locale.language.script);
93    assert_eq!(Some("US".to_string()), locale.language.region);
94    assert_eq!(None, locale.language.variants);
95    let u = locale.extensions.unicode_locale.unwrap();
96    assert_eq!(
97        &vec!["h12".to_string()],
98        u.get(0).unwrap().ufield.get("hc").unwrap()
99    );
100
101    // full case
102    let locale = parse_unicode_locale_id("ja-Latn-JP-macos-U-attr1-kz-value2-t-en-Latn-US-linux-t1-value1-value2-a-vue-rust-x-foo-123")
103            .unwrap();
104    assert_eq!("ja-Latn-JP-macos", format!("{}", locale.language));
105    assert_eq!(
106        "u-attr1-kz-value2-t-en-Latn-US-linux-t1-value1-value2-a-vue-rust-x-foo-123",
107        format!("{}", locale.extensions)
108    );
109
110    // Display trait implementation
111    assert_eq!(
112        "ja-Latn-JP-macos-u-attr1-kz-value2-t-en-Latn-US-linux-t1-value1-value2-a-vue-rust-x-foo-123",
113        format!("{}", parse_unicode_locale_id("ja-Latn-JP-macos-U-attr1-kz-value2-t-en-Latn-US-linux-t1-value1-value2-a-vue-rust-x-foo-123")
114            .unwrap())
115    );
116
117    // FromStr trait implementation
118    let result: UnicodeLocaleIdentifier = "ja-Latn-JP".parse().unwrap();
119    assert_eq!("ja-Latn-JP", format!("{}", result));
120}
121
122#[test]
123fn fail_parse_unicode_locale_id() {
124    // missing locale
125    assert_eq!(
126        ParserError::Missing,
127        parse_unicode_locale_id("").unwrap_err()
128    );
129}