icu_locid/
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 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}