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 ] |
38 | macro_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 ] |
123 | macro_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)] |
167 | mod 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 | |