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 | //! Data provider returning multilingual "Hello World" strings for testing. |
6 | |
7 | #![allow (clippy::exhaustive_structs)] // data struct module |
8 | |
9 | use crate as icu_provider; |
10 | |
11 | use crate::prelude::*; |
12 | use alloc::borrow::Cow; |
13 | use alloc::string::String; |
14 | use core::fmt::Debug; |
15 | use writeable::Writeable; |
16 | use yoke::*; |
17 | use zerofrom::*; |
18 | |
19 | /// A struct containing "Hello World" in the requested language. |
20 | #[derive (Debug, PartialEq, Clone, Yokeable, ZeroFrom)] |
21 | #[cfg_attr (feature = "serde" , derive(serde::Deserialize))] |
22 | #[cfg_attr ( |
23 | any(feature = "deserialize_json" , feature = "datagen" ), |
24 | derive(serde::Serialize) |
25 | )] |
26 | #[cfg_attr (feature = "datagen" , derive(databake::Bake))] |
27 | #[cfg_attr (feature = "datagen" , databake(path = icu_provider::hello_world))] |
28 | pub struct HelloWorldV1<'data> { |
29 | /// The translation of "Hello World". |
30 | #[cfg_attr (feature = "serde" , serde(borrow))] |
31 | pub message: Cow<'data, str>, |
32 | } |
33 | |
34 | impl Default for HelloWorldV1<'_> { |
35 | fn default() -> Self { |
36 | HelloWorldV1 { |
37 | message: Cow::Borrowed("(und) Hello World" ), |
38 | } |
39 | } |
40 | } |
41 | |
42 | /// Marker type for [`HelloWorldV1`]. |
43 | #[cfg_attr (feature = "datagen" , derive(Default, databake::Bake))] |
44 | #[cfg_attr (feature = "datagen" , databake(path = icu_provider::hello_world))] |
45 | #[derive (Debug)] |
46 | pub struct HelloWorldV1Marker; |
47 | |
48 | impl DataMarker for HelloWorldV1Marker { |
49 | type Yokeable = HelloWorldV1<'static>; |
50 | } |
51 | |
52 | impl KeyedDataMarker for HelloWorldV1Marker { |
53 | const KEY: DataKey = icu_provider::data_key!("core/helloworld@1" ); |
54 | } |
55 | |
56 | /// A data provider returning Hello World strings in different languages. |
57 | /// |
58 | /// Mostly useful for testing. |
59 | /// |
60 | /// # Examples |
61 | /// |
62 | /// ``` |
63 | /// use icu_locid::locale; |
64 | /// use icu_provider::hello_world::*; |
65 | /// use icu_provider::prelude::*; |
66 | /// |
67 | /// let german_hello_world: DataPayload<HelloWorldV1Marker> = |
68 | /// HelloWorldProvider |
69 | /// .load(DataRequest { |
70 | /// locale: &locale!("de" ).into(), |
71 | /// metadata: Default::default(), |
72 | /// }) |
73 | /// .expect("Loading should succeed" ) |
74 | /// .take_payload() |
75 | /// .expect("Data should be present" ); |
76 | /// |
77 | /// assert_eq!("Hallo Welt" , german_hello_world.get().message); |
78 | /// ``` |
79 | /// |
80 | /// Load the reverse string using an auxiliary key: |
81 | /// |
82 | /// ``` |
83 | /// use icu_provider::hello_world::*; |
84 | /// use icu_provider::prelude::*; |
85 | /// |
86 | /// let reverse_hello_world: DataPayload<HelloWorldV1Marker> = |
87 | /// HelloWorldProvider |
88 | /// .load(DataRequest { |
89 | /// locale: &"en+reverse" .parse().unwrap(), |
90 | /// metadata: Default::default(), |
91 | /// }) |
92 | /// .expect("Loading should succeed" ) |
93 | /// .take_payload() |
94 | /// .expect("Data should be present" ); |
95 | /// |
96 | /// assert_eq!("Olleh Dlrow" , reverse_hello_world.get().message); |
97 | /// ``` |
98 | #[derive (Debug, PartialEq, Default)] |
99 | pub struct HelloWorldProvider; |
100 | |
101 | impl HelloWorldProvider { |
102 | // Data from https://en.wiktionary.org/wiki/Hello_World#Translations |
103 | // Keep this sorted! |
104 | const DATA: &'static [(&'static str, &'static str)] = &[ |
105 | ("bn" , "ওহে বিশ্ব" ), |
106 | ("cs" , "Ahoj světe" ), |
107 | ("de" , "Hallo Welt" ), |
108 | ("de-AT" , "Servus Welt" ), |
109 | ("el" , "Καλημέρα κόσμε" ), |
110 | ("en" , "Hello World" ), |
111 | ("en+reverse" , "Olleh Dlrow" ), |
112 | ("en-001" , "Hello from 🗺️" ), // WORLD |
113 | ("en-002" , "Hello from 🌍" ), // AFRICA |
114 | ("en-019" , "Hello from 🌎" ), // AMERICAS |
115 | ("en-142" , "Hello from 🌏" ), // ASIA |
116 | ("en-GB" , "Hello from 🇬🇧" ), // GREAT BRITAIN |
117 | ("en-GB-u-sd-gbeng" , "Hello from 🏴" ), // ENGLAND |
118 | ("eo" , "Saluton, Mondo" ), |
119 | ("fa" , "سلام دنیا" ), |
120 | ("fi" , "hei maailma" ), |
121 | ("is" , "Halló, heimur" ), |
122 | ("ja" , "こんにちは世界" ), |
123 | ("ja+reverse" , "界世はちにんこ" ), |
124 | ("la" , "Ave, munde" ), |
125 | ("pt" , "Olá, mundo" ), |
126 | ("ro" , "Salut, lume" ), |
127 | ("ru" , "Привет, мир" ), |
128 | ("sr" , "Поздрав свете" ), |
129 | ("sr-Latn" , "Pozdrav svete" ), |
130 | ("vi" , "Xin chào thế giới" ), |
131 | ("zh" , "你好世界" ), |
132 | ]; |
133 | |
134 | /// Converts this provider into a [`BufferProvider`] that uses JSON serialization. |
135 | #[cfg (feature = "deserialize_json" )] |
136 | pub fn into_json_provider(self) -> HelloWorldJsonProvider { |
137 | HelloWorldJsonProvider |
138 | } |
139 | } |
140 | |
141 | impl DataProvider<HelloWorldV1Marker> for HelloWorldProvider { |
142 | fn load(&self, req: DataRequest) -> Result<DataResponse<HelloWorldV1Marker>, DataError> { |
143 | #[allow (clippy::indexing_slicing)] // binary_search |
144 | let data: &str = Self::DATA |
145 | .binary_search_by(|(k, _)| req.locale.strict_cmp(k.as_bytes()).reverse()) |
146 | .map(|i| Self::DATA[i].1) |
147 | .map_err(|_| DataErrorKind::MissingLocale.with_req(HelloWorldV1Marker::KEY, req))?; |
148 | Ok(DataResponse { |
149 | metadata: Default::default(), |
150 | payload: Some(DataPayload::from_static_str(data)), |
151 | }) |
152 | } |
153 | } |
154 | |
155 | impl DataPayload<HelloWorldV1Marker> { |
156 | /// Make a [`DataPayload`]`<`[`HelloWorldV1Marker`]`>` from a static string slice. |
157 | pub fn from_static_str(s: &'static str) -> DataPayload<HelloWorldV1Marker> { |
158 | DataPayload::from_owned(data:HelloWorldV1 { |
159 | message: Cow::Borrowed(s), |
160 | }) |
161 | } |
162 | } |
163 | |
164 | // AnyProvider support. |
165 | #[cfg (not(feature = "datagen" ))] |
166 | icu_provider::impl_dynamic_data_provider!(HelloWorldProvider, [HelloWorldV1Marker,], AnyMarker); |
167 | |
168 | #[cfg (feature = "deserialize_json" )] |
169 | /// A data provider returning Hello World strings in different languages as JSON blobs. |
170 | /// |
171 | /// Mostly useful for testing. |
172 | /// |
173 | /// # Examples |
174 | /// |
175 | /// ``` |
176 | /// use icu_locid::locale; |
177 | /// use icu_provider::hello_world::*; |
178 | /// use icu_provider::prelude::*; |
179 | /// |
180 | /// let german_hello_world = HelloWorldProvider |
181 | /// .into_json_provider() |
182 | /// .load_buffer(HelloWorldV1Marker::KEY, DataRequest { |
183 | /// locale: &locale!("de").into(), |
184 | /// metadata: Default::default(), |
185 | /// }) |
186 | /// .expect("Loading should succeed") |
187 | /// .take_payload() |
188 | /// .expect("Data should be present"); |
189 | /// |
190 | /// assert_eq!(german_hello_world.get(), br#"{"message":"Hallo Welt"}"#); |
191 | #[derive (Debug)] |
192 | pub struct HelloWorldJsonProvider; |
193 | |
194 | #[cfg (feature = "deserialize_json" )] |
195 | impl BufferProvider for HelloWorldJsonProvider { |
196 | fn load_buffer( |
197 | &self, |
198 | key: DataKey, |
199 | req: DataRequest, |
200 | ) -> Result<DataResponse<BufferMarker>, DataError> { |
201 | key.match_key(HelloWorldV1Marker::KEY)?; |
202 | let result = HelloWorldProvider.load(req)?; |
203 | let (mut metadata, old_payload) = |
204 | DataResponse::<HelloWorldV1Marker>::take_metadata_and_payload(result)?; |
205 | metadata.buffer_format = Some(icu_provider::buf::BufferFormat::Json); |
206 | #[allow (clippy::unwrap_used)] // HelloWorldV1::serialize is infallible |
207 | Ok(DataResponse { |
208 | metadata, |
209 | payload: Some(DataPayload::from_owned_buffer( |
210 | serde_json::to_string(old_payload.get()) |
211 | .unwrap() |
212 | .into_bytes() |
213 | .into_boxed_slice(), |
214 | )), |
215 | }) |
216 | } |
217 | } |
218 | |
219 | #[cfg (feature = "datagen" )] |
220 | impl icu_provider::datagen::IterableDataProvider<HelloWorldV1Marker> for HelloWorldProvider { |
221 | fn supported_locales(&self) -> Result<Vec<DataLocale>, DataError> { |
222 | #[allow (clippy::unwrap_used)] // datagen |
223 | Ok(Self::DATA.iter().map(|(s, _)| s.parse().unwrap()).collect()) |
224 | } |
225 | } |
226 | |
227 | #[cfg (feature = "datagen" )] |
228 | icu_provider::make_exportable_provider!(HelloWorldProvider, [HelloWorldV1Marker,]); |
229 | |
230 | /// A type that formats localized "hello world" strings. |
231 | /// |
232 | /// This type is intended to take the shape of a typical ICU4X formatter API. |
233 | /// |
234 | /// # Examples |
235 | /// |
236 | /// ``` |
237 | /// use icu_locid::locale; |
238 | /// use icu_provider::hello_world::{HelloWorldFormatter, HelloWorldProvider}; |
239 | /// use writeable::assert_writeable_eq; |
240 | /// |
241 | /// let fmt = HelloWorldFormatter::try_new_unstable( |
242 | /// &HelloWorldProvider, |
243 | /// &locale!("eo" ).into(), |
244 | /// ) |
245 | /// .expect("locale exists" ); |
246 | /// |
247 | /// assert_writeable_eq!(fmt.format(), "Saluton, Mondo" ); |
248 | /// ``` |
249 | #[derive (Debug)] |
250 | pub struct HelloWorldFormatter { |
251 | data: DataPayload<HelloWorldV1Marker>, |
252 | } |
253 | |
254 | /// A formatted hello world message. Implements [`Writeable`]. |
255 | /// |
256 | /// For an example, see [`HelloWorldFormatter`]. |
257 | #[derive (Debug)] |
258 | pub struct FormattedHelloWorld<'l> { |
259 | data: &'l HelloWorldV1<'l>, |
260 | } |
261 | |
262 | impl HelloWorldFormatter { |
263 | /// Creates a new [`HelloWorldFormatter`] for the specified locale. |
264 | /// |
265 | /// [📚 Help choosing a constructor](icu_provider::constructors) |
266 | pub fn try_new(locale: &DataLocale) -> Result<Self, DataError> { |
267 | Self::try_new_unstable(&HelloWorldProvider, locale) |
268 | } |
269 | |
270 | icu_provider::gen_any_buffer_data_constructors!(locale: include, options: skip, error: DataError, |
271 | #[cfg (skip)] |
272 | functions: [ |
273 | try_new, |
274 | try_new_with_any_provider, |
275 | try_new_with_buffer_provider, |
276 | try_new_unstable, |
277 | Self, |
278 | ]); |
279 | |
280 | #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new)] |
281 | pub fn try_new_unstable<P>(provider: &P, locale: &DataLocale) -> Result<Self, DataError> |
282 | where |
283 | P: DataProvider<HelloWorldV1Marker>, |
284 | { |
285 | let data = provider |
286 | .load(DataRequest { |
287 | locale, |
288 | metadata: Default::default(), |
289 | })? |
290 | .take_payload()?; |
291 | Ok(Self { data }) |
292 | } |
293 | |
294 | /// Formats a hello world message, returning a [`FormattedHelloWorld`]. |
295 | #[allow (clippy::needless_lifetimes)] // documentary example |
296 | pub fn format<'l>(&'l self) -> FormattedHelloWorld<'l> { |
297 | FormattedHelloWorld { |
298 | data: self.data.get(), |
299 | } |
300 | } |
301 | |
302 | /// Formats a hello world message, returning a [`String`]. |
303 | pub fn format_to_string(&self) -> String { |
304 | self.format().write_to_string().into_owned() |
305 | } |
306 | } |
307 | |
308 | impl<'l> Writeable for FormattedHelloWorld<'l> { |
309 | fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result { |
310 | self.data.message.write_to(sink) |
311 | } |
312 | |
313 | fn write_to_string(&self) -> Cow<str> { |
314 | self.data.message.clone() |
315 | } |
316 | |
317 | fn writeable_length_hint(&self) -> writeable::LengthHint { |
318 | self.data.message.writeable_length_hint() |
319 | } |
320 | } |
321 | |
322 | writeable::impl_display_with_writeable!(FormattedHelloWorld<'_>); |
323 | |
324 | #[cfg (feature = "datagen" )] |
325 | #[test ] |
326 | fn test_iter() { |
327 | use crate::datagen::IterableDataProvider; |
328 | use icu_locid::locale; |
329 | |
330 | assert_eq!( |
331 | HelloWorldProvider.supported_locales().unwrap(), |
332 | vec![ |
333 | locale!("bn" ).into(), |
334 | locale!("cs" ).into(), |
335 | locale!("de" ).into(), |
336 | locale!("de-AT" ).into(), |
337 | locale!("el" ).into(), |
338 | locale!("en" ).into(), |
339 | "en+reverse" .parse().unwrap(), |
340 | locale!("en-001" ).into(), |
341 | locale!("en-002" ).into(), |
342 | locale!("en-019" ).into(), |
343 | locale!("en-142" ).into(), |
344 | locale!("en-GB" ).into(), |
345 | locale!("en-GB-u-sd-gbeng" ).into(), |
346 | locale!("eo" ).into(), |
347 | locale!("fa" ).into(), |
348 | locale!("fi" ).into(), |
349 | locale!("is" ).into(), |
350 | locale!("ja" ).into(), |
351 | "ja+reverse" .parse().unwrap(), |
352 | locale!("la" ).into(), |
353 | locale!("pt" ).into(), |
354 | locale!("ro" ).into(), |
355 | locale!("ru" ).into(), |
356 | locale!("sr" ).into(), |
357 | locale!("sr-Latn" ).into(), |
358 | locale!("vi" ).into(), |
359 | locale!("zh" ).into() |
360 | ] |
361 | ); |
362 | } |
363 | |