icu_provider/
hello_world.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//! Data provider returning multilingual "Hello World" strings for testing.
6
7#![allow(clippy::exhaustive_structs)] // data struct module
8
9use crate as icu_provider;
10
11use crate::prelude::*;
12use alloc::borrow::Cow;
13use alloc::string::String;
14use core::fmt::Debug;
15use writeable::Writeable;
16use yoke::*;
17use 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))]
28pub struct HelloWorldV1<'data> {
29    /// The translation of "Hello World".
30    #[cfg_attr(feature = "serde", serde(borrow))]
31    pub message: Cow<'data, str>,
32}
33
34impl 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)]
46pub struct HelloWorldV1Marker;
47
48impl DataMarker for HelloWorldV1Marker {
49    type Yokeable = HelloWorldV1<'static>;
50}
51
52impl 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::langid;
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: &langid!("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-x-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)]
99pub struct HelloWorldProvider;
100
101impl 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-001", "Hello from 🗺️"),            // WORLD
112        ("en-002", "Hello from 🌍"),           // AFRICA
113        ("en-019", "Hello from 🌎"),           // AMERICAS
114        ("en-142", "Hello from 🌏"),           // ASIA
115        ("en-GB", "Hello from 🇬🇧"),            // GREAT BRITAIN
116        ("en-GB-u-sd-gbeng", "Hello from 🏴󠁧󠁢󠁥󠁮󠁧󠁿"), // ENGLAND
117        ("en-x-reverse", "Olleh Dlrow"),
118        ("eo", "Saluton, Mondo"),
119        ("fa", "سلام دنیا‎"),
120        ("fi", "hei maailma"),
121        ("is", "Halló, heimur"),
122        ("ja", "こんにちは世界"),
123        ("ja-x-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
141impl DataProvider<HelloWorldV1Marker> for HelloWorldProvider {
142    fn load(&self, req: DataRequest) -> Result<DataResponse<HelloWorldV1Marker>, DataError> {
143        #[allow(clippy::indexing_slicing)] // binary_search
144        let data = 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
155impl 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(HelloWorldV1 {
159            message: Cow::Borrowed(s),
160        })
161    }
162}
163
164// AnyProvider support.
165#[cfg(not(feature = "datagen"))]
166icu_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::langid;
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: &langid!("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)]
192pub struct HelloWorldJsonProvider;
193
194#[cfg(feature = "deserialize_json")]
195impl 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")]
220impl 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")]
228icu_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)]
250pub 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)]
258pub struct FormattedHelloWorld<'l> {
259    data: &'l HelloWorldV1<'l>,
260}
261
262impl 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
308impl<'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
322writeable::impl_display_with_writeable!(FormattedHelloWorld<'_>);
323
324#[cfg(feature = "datagen")]
325#[test]
326fn 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            locale!("en-001").into(),
340            locale!("en-002").into(),
341            locale!("en-019").into(),
342            locale!("en-142").into(),
343            locale!("en-GB").into(),
344            locale!("en-GB-u-sd-gbeng").into(),
345            "en-x-reverse".parse().unwrap(),
346            locale!("eo").into(),
347            locale!("fa").into(),
348            locale!("fi").into(),
349            locale!("is").into(),
350            locale!("ja").into(),
351            "ja-x-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}