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::{LanguageIdentifier, langid};
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 _ => panic!(concat!("Invalid language identifier: ", $langid, " . Note langid! macro can only support up to a single variant tag. Use runtime parsing instead.")),
51 }
52 }};
53}
54
55/// A macro allowing for compile-time construction of valid [`Locale`]s.
56///
57/// The macro will perform syntax normalization of the tag.
58///
59/// # Examples
60///
61/// ```
62/// use icu::locale::{Locale, locale};
63///
64/// const DE_AT: Locale = locale!("de-at");
65///
66/// let de_at: Locale = "de-at".parse().unwrap();
67///
68/// assert_eq!(DE_AT, de_at);
69/// ```
70///
71/// *Note*: The macro cannot produce locales with more than one variant or multiple extensions
72/// (only single keyword unicode extension is supported) due to const
73/// limitations (see [`Heap Allocations in Constants`]):
74///
75/// ```compile_fail,E0080
76/// icu::locale::locale!("sl-IT-rozaj-biske-1994");
77/// ```
78/// Use runtime parsing instead:
79/// ```
80/// "sl-IT-rozaj-biske-1994"
81/// .parse::<icu::locale::Locale>()
82/// .unwrap();
83/// ```
84///
85/// Locales with multiple keys are not supported
86/// ```compile_fail,E0080
87/// icu::locale::locale!("th-TH-u-ca-buddhist-nu-thai");
88/// ```
89/// Use runtime parsing instead:
90/// ```
91/// "th-TH-u-ca-buddhist-nu-thai"
92/// .parse::<icu::locale::Locale>()
93/// .unwrap();
94/// ```
95///
96/// Locales with attributes are not supported
97/// ```compile_fail,E0080
98/// icu::locale::locale!("en-US-u-foobar-ca-buddhist");
99/// ```
100/// Use runtime parsing instead:
101/// ```
102/// "en-US-u-foobar-ca-buddhist"
103/// .parse::<icu::locale::Locale>()
104/// .unwrap();
105/// ```
106///
107/// Locales with single key but multiple types are not supported
108/// ```compile_fail,E0080
109/// icu::locale::locale!("en-US-u-ca-islamic-umalqura");
110/// ```
111/// Use runtime parsing instead:
112/// ```
113/// "en-US-u-ca-islamic-umalqura"
114/// .parse::<icu::locale::Locale>()
115/// .unwrap();
116/// ```
117/// [`Locale`]: crate::Locale
118/// [`Heap Allocations in Constants`]: https://github.com/rust-lang/const-eval/issues/20
119#[macro_export]
120macro_rules! locale {
121 ($locale:literal) => { const {
122 match $crate::Locale::try_from_utf8_with_single_variant_single_keyword_unicode_extension(
123 $locale.as_bytes(),
124 ) {
125 Ok((language, script, region, variant, keyword)) => $crate::Locale {
126 id: $crate::LanguageIdentifier {
127 language,
128 script,
129 region,
130 variants: match variant {
131 Some(v) => $crate::subtags::Variants::from_variant(v),
132 None => $crate::subtags::Variants::new(),
133 },
134 },
135 extensions: match keyword {
136 Some((key, value)) => $crate::extensions::Extensions::from_unicode(
137 $crate::extensions::unicode::Unicode {
138 keywords: $crate::extensions::unicode::Keywords::new_single(
139 key,
140 $crate::extensions::unicode::Value::from_subtag(value),
141 ),
142
143 attributes: $crate::extensions::unicode::Attributes::new(),
144 },
145 ),
146 None => $crate::extensions::Extensions::new(),
147 },
148 },
149 _ => panic!(concat!(
150 "Invalid locale: ",
151 $locale,
152 " . Note the locale! macro only supports up to one variant tag; \
153 and one unicode keyword, other extension are \
154 not supported. Use runtime parsing instead."
155 )),
156 }
157 }};
158}
159
160/// A macro allowing for compile-time construction of valid [`DataLocale`](crate::DataLocale)s.
161///
162/// The macro will perform syntax normalization of the tag.
163///
164/// # Examples
165///
166/// ```
167/// use icu::locale::{DataLocale, data_locale};
168///
169/// const DE_AT: DataLocale = data_locale!("de-at");
170///
171/// let de_at: DataLocale = "de-at".parse().unwrap();
172///
173/// assert_eq!(DE_AT, de_at);
174/// ```
175#[macro_export]
176macro_rules! data_locale {
177 ($locale:literal) => {
178 const {
179 let Ok(d) = $crate::DataLocale::try_from_str($locale) else {
180 panic!(concat!("Invalid data locale: ", $locale));
181 };
182 d
183 }
184 };
185}
186
187#[cfg(test)]
188mod test {
189 use crate::LanguageIdentifier;
190 use crate::Locale;
191
192 #[test]
193 fn test_langid_macro_can_parse_langid_with_single_variant() {
194 const DE_AT_FOOBAR: LanguageIdentifier = langid!("de-at-foobar");
195 let de_at_foobar: LanguageIdentifier = "de-at-foobar".parse().unwrap();
196 assert_eq!(DE_AT_FOOBAR, de_at_foobar);
197 }
198
199 #[test]
200 fn test_locale_macro_can_parse_locale_with_single_variant() {
201 const DE_AT_FOOBAR: Locale = locale!("de-at-foobar");
202 let de_at_foobar: Locale = "de-at-foobar".parse().unwrap();
203 assert_eq!(DE_AT_FOOBAR, de_at_foobar);
204 }
205
206 #[test]
207 fn test_locale_macro_can_parse_locale_with_single_keyword_unicode_extension() {
208 const DE_AT_U_CA_FOOBAR: Locale = locale!("de-at-u-ca-foobar");
209 let de_at_u_ca_foobar: Locale = "de-at-u-ca-foobar".parse().unwrap();
210 assert_eq!(DE_AT_U_CA_FOOBAR, de_at_u_ca_foobar);
211 }
212}