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