icu_locale_core/
macros.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
5/// A macro allowing for compile-time construction of valid [`LanguageIdentifier`]s.
6///
7/// The macro will perform syntax normalization of the tag.
8///
9/// # Examples
10///
11/// ```
12/// use icu::locale::{langid, LanguageIdentifier};
13///
14/// const DE_AT: LanguageIdentifier = langid!("de-at");
15///
16/// let de_at: LanguageIdentifier = "de-at".parse().unwrap();
17///
18/// assert_eq!(DE_AT, de_at);
19/// ```
20///
21/// *Note*: The macro cannot produce language identifiers with more than one variants due to const
22/// limitations (see [`Heap Allocations in Constants`]):
23///
24/// ```compile_fail,E0080
25/// icu::locale::langid!("und-variant1-variant2");
26/// ```
27///
28/// Use runtime parsing instead:
29/// ```
30/// "und-variant1-variant2"
31///     .parse::<icu::locale::LanguageIdentifier>()
32///     .unwrap();
33/// ```
34///
35/// [`LanguageIdentifier`]: crate::LanguageIdentifier
36/// [`Heap Allocations in Constants`]: https://github.com/rust-lang/const-eval/issues/20
37#[macro_export]
38macro_rules! langid {
39    ($langid:literal) => { const {
40        match $crate::LanguageIdentifier::try_from_utf8_with_single_variant($langid.as_bytes()) {
41            Ok((language, script, region, variant)) => $crate::LanguageIdentifier {
42                language,
43                script,
44                region,
45                variants: match variant {
46                    Some(v) => $crate::subtags::Variants::from_variant(v),
47                    None => $crate::subtags::Variants::new(),
48                }
49            },
50            #[allow(clippy::panic)] // const context
51            _ => panic!(concat!("Invalid language code: ", $langid, " . Note langid! macro can only support up to a single variant tag. Use runtime parsing instead.")),
52        }
53    }};
54}
55
56/// A macro allowing for compile-time construction of valid [`Locale`]s.
57///
58/// The macro will perform syntax normalization of the tag.
59///
60/// # Examples
61///
62/// ```
63/// use icu::locale::{locale, Locale};
64///
65/// const DE_AT: Locale = locale!("de-at");
66///
67/// let de_at: Locale = "de-at".parse().unwrap();
68///
69/// assert_eq!(DE_AT, de_at);
70/// ```
71///
72/// *Note*: The macro cannot produce locales with more than one variant or multiple extensions
73/// (only single keyword unicode extension is supported) due to const
74/// limitations (see [`Heap Allocations in Constants`]):
75///
76/// ```compile_fail,E0080
77/// icu::locale::locale!("sl-IT-rozaj-biske-1994");
78/// ```
79/// Use runtime parsing instead:
80/// ```
81/// "sl-IT-rozaj-biske-1994"
82///     .parse::<icu::locale::Locale>()
83///     .unwrap();
84/// ```
85///
86/// Locales with multiple keys are not supported
87/// ```compile_fail,E0080
88/// icu::locale::locale!("th-TH-u-ca-buddhist-nu-thai");
89/// ```
90/// Use runtime parsing instead:
91/// ```
92/// "th-TH-u-ca-buddhist-nu-thai"
93///     .parse::<icu::locale::Locale>()
94///     .unwrap();
95/// ```
96///
97/// Locales with attributes are not supported
98/// ```compile_fail,E0080
99/// icu::locale::locale!("en-US-u-foobar-ca-buddhist");
100/// ```
101/// Use runtime parsing instead:
102/// ```
103/// "en-US-u-foobar-ca-buddhist"
104///     .parse::<icu::locale::Locale>()
105///     .unwrap();
106/// ```
107///
108/// Locales with single key but multiple types are not supported
109/// ```compile_fail,E0080
110/// icu::locale::locale!("en-US-u-ca-islamic-umalqura");
111/// ```
112/// Use runtime parsing instead:
113/// ```
114/// "en-US-u-ca-islamic-umalqura"
115///     .parse::<icu::locale::Locale>()
116///     .unwrap();
117/// ```
118/// [`Locale`]: crate::Locale
119/// [`Heap Allocations in Constants`]: https://github.com/rust-lang/const-eval/issues/20
120#[macro_export]
121macro_rules! locale {
122    ($locale:literal) => { const {
123        match $crate::Locale::try_from_utf8_with_single_variant_single_keyword_unicode_extension(
124            $locale.as_bytes(),
125        ) {
126            Ok((language, script, region, variant, keyword)) => $crate::Locale {
127                id: $crate::LanguageIdentifier {
128                    language,
129                    script,
130                    region,
131                    variants: match variant {
132                        Some(v) => $crate::subtags::Variants::from_variant(v),
133                        None => $crate::subtags::Variants::new(),
134                    },
135                },
136                extensions: match keyword {
137                    Some(k) => $crate::extensions::Extensions::from_unicode(
138                        $crate::extensions::unicode::Unicode {
139                            keywords: $crate::extensions::unicode::Keywords::new_single(
140                                k.0,
141                                $crate::extensions::unicode::Value::from_subtag(k.1),
142                            ),
143
144                            attributes: $crate::extensions::unicode::Attributes::new(),
145                        },
146                    ),
147                    None => $crate::extensions::Extensions::new(),
148                },
149            },
150            #[allow(clippy::panic)] // const context
151            _ => panic!(concat!(
152                "Invalid language code: ",
153                $locale,
154                " . Note the locale! macro only supports up to one variant tag; \
155                                    and one unicode keyword, other extension are \
156                                    not supported. Use runtime parsing instead."
157            )),
158        }
159    }};
160}
161
162#[cfg(test)]
163mod test {
164    use crate::LanguageIdentifier;
165    use crate::Locale;
166
167    #[test]
168    fn test_langid_macro_can_parse_langid_with_single_variant() {
169        const DE_AT_FOOBAR: LanguageIdentifier = langid!("de-at-foobar");
170        let de_at_foobar: LanguageIdentifier = "de-at-foobar".parse().unwrap();
171        assert_eq!(DE_AT_FOOBAR, de_at_foobar);
172    }
173
174    #[test]
175    fn test_locale_macro_can_parse_locale_with_single_variant() {
176        const DE_AT_FOOBAR: Locale = locale!("de-at-foobar");
177        let de_at_foobar: Locale = "de-at-foobar".parse().unwrap();
178        assert_eq!(DE_AT_FOOBAR, de_at_foobar);
179    }
180
181    #[test]
182    fn test_locale_macro_can_parse_locale_with_single_keyword_unicode_extension() {
183        const DE_AT_U_CA_FOOBAR: Locale = locale!("de-at-u-ca-foobar");
184        let de_at_u_ca_foobar: Locale = "de-at-u-ca-foobar".parse().unwrap();
185        assert_eq!(DE_AT_U_CA_FOOBAR, de_at_u_ca_foobar);
186    }
187}