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