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        let data = Self::DATA
159            .iter()
160            .find(|(l, a, _)| {
161                req.id.locale.strict_cmp(l.as_bytes()).is_eq()
162                    && *a == req.id.marker_attributes.as_str()
163            })
164            .map(|(_, _, v)| v)
165            .ok_or_else(|| DataErrorKind::IdentifierNotFound.with_req(HelloWorldV1::INFO, req))?;
166        Ok(DataResponse {
167            metadata: DataResponseMetadata::default().with_checksum(1234),
168            payload: DataPayload::from_static_str(data),
169        })
170    }
171}
172
173impl DryDataProvider<HelloWorldV1> for HelloWorldProvider {
174    fn dry_load(&self, req: DataRequest) -> Result<DataResponseMetadata, DataError> {
175        self.load(req).map(|r| r.metadata)
176    }
177}
178
179impl DataPayload<HelloWorldV1> {
180    /// Make a [`DataPayload`]`<`[`HelloWorldV1`]`>` from a static string slice.
181    pub fn from_static_str(s: &'static str) -> DataPayload<HelloWorldV1> {
182        DataPayload::from_owned(HelloWorld {
183            message: Cow::Borrowed(s),
184        })
185    }
186}
187
188#[cfg(feature = "deserialize_json")]
189/// A data provider returning Hello World strings in different languages as JSON blobs.
190///
191/// Mostly useful for testing.
192///
193/// # Examples
194///
195/// ```
196/// use icu_locale_core::langid;
197/// use icu_provider::hello_world::*;
198/// use icu_provider::prelude::*;
199///
200/// let german_hello_world = HelloWorldProvider
201///     .into_json_provider()
202///     .load_data(HelloWorldV1::INFO, DataRequest {
203///         id: DataIdentifierBorrowed::for_locale(&langid!("de").into()),
204///         ..Default::default()
205///     })
206///     .expect("Loading should succeed");
207///
208/// assert_eq!(german_hello_world.payload.get(), br#"{"message":"Hallo Welt"}"#);
209#[derive(Debug)]
210pub struct HelloWorldJsonProvider;
211
212#[cfg(feature = "deserialize_json")]
213impl DynamicDataProvider<BufferMarker> for HelloWorldJsonProvider {
214    fn load_data(
215        &self,
216        marker: DataMarkerInfo,
217        req: DataRequest,
218    ) -> Result<DataResponse<BufferMarker>, DataError> {
219        marker.match_marker(HelloWorldV1::INFO)?;
220        let result = HelloWorldProvider.load(req)?;
221        Ok(DataResponse {
222            metadata: DataResponseMetadata {
223                buffer_format: Some(icu_provider::buf::BufferFormat::Json),
224                ..result.metadata
225            },
226            #[expect(clippy::unwrap_used)] // HelloWorld::serialize is infallible
227            payload: DataPayload::from_owned_buffer(
228                serde_json::to_string(result.payload.get())
229                    .unwrap()
230                    .into_bytes()
231                    .into_boxed_slice(),
232            ),
233        })
234    }
235}
236
237impl IterableDataProvider<HelloWorldV1> for HelloWorldProvider {
238    fn iter_ids(&self) -> Result<BTreeSet<DataIdentifierCow<'_>>, DataError> {
239        #[expect(clippy::unwrap_used)] // hello-world
240        Ok(Self::DATA
241            .iter()
242            .map(|(l, a, _)| {
243                DataIdentifierCow::from_borrowed_and_owned(
244                    DataMarkerAttributes::from_str_or_panic(a),
245                    l.parse().unwrap(),
246                )
247            })
248            .collect())
249    }
250}
251
252#[cfg(feature = "export")]
253icu_provider::export::make_exportable_provider!(HelloWorldProvider, [HelloWorldV1,]);
254
255define_preferences!(
256    /// Hello World Preferences.
257    [Copy]
258    HelloWorldFormatterPreferences, {}
259);
260
261/// A type that formats localized "hello world" strings.
262///
263/// This type is intended to take the shape of a typical ICU4X formatter API.
264///
265/// # Examples
266///
267/// ```
268/// use icu_locale_core::locale;
269/// use icu_provider::hello_world::{HelloWorldFormatter, HelloWorldProvider};
270/// use writeable::assert_writeable_eq;
271///
272/// let fmt = HelloWorldFormatter::try_new_unstable(
273///     &HelloWorldProvider,
274///     locale!("eo").into(),
275/// )
276/// .expect("locale exists");
277///
278/// assert_writeable_eq!(fmt.format(), "Saluton, Mondo");
279/// ```
280#[derive(Debug)]
281pub struct HelloWorldFormatter {
282    data: DataPayload<HelloWorldV1>,
283}
284
285/// A formatted hello world message. Implements [`Writeable`].
286///
287/// For an example, see [`HelloWorldFormatter`].
288#[derive(Debug)]
289pub struct FormattedHelloWorld<'l> {
290    data: &'l HelloWorld<'l>,
291}
292
293impl HelloWorldFormatter {
294    /// Creates a new [`HelloWorldFormatter`] for the specified locale.
295    ///
296    /// [📚 Help choosing a constructor](icu_provider::constructors)
297    pub fn try_new(prefs: HelloWorldFormatterPreferences) -> Result<Self, DataError> {
298        Self::try_new_unstable(&HelloWorldProvider, prefs)
299    }
300
301    icu_provider::gen_buffer_data_constructors!((prefs: HelloWorldFormatterPreferences) -> error: DataError,
302        functions: [
303            try_new: skip,
304            try_new_with_buffer_provider,
305            try_new_unstable,
306            Self,
307    ]);
308
309    #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
310    pub fn try_new_unstable<P>(
311        provider: &P,
312        prefs: HelloWorldFormatterPreferences,
313    ) -> Result<Self, DataError>
314    where
315        P: DataProvider<HelloWorldV1>,
316    {
317        let locale = HelloWorldV1::make_locale(prefs.locale_preferences);
318        let data = provider
319            .load(DataRequest {
320                id: crate::request::DataIdentifierBorrowed::for_locale(&locale),
321                ..Default::default()
322            })?
323            .payload;
324        Ok(Self { data })
325    }
326
327    /// Formats a hello world message, returning a [`FormattedHelloWorld`].
328    pub fn format<'l>(&'l self) -> FormattedHelloWorld<'l> {
329        FormattedHelloWorld {
330            data: self.data.get(),
331        }
332    }
333
334    /// Formats a hello world message, returning a [`String`].
335    pub fn format_to_string(&self) -> String {
336        self.format().write_to_string().into_owned()
337    }
338}
339
340impl Writeable for FormattedHelloWorld<'_> {
341    fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
342        self.data.message.write_to(sink)
343    }
344
345    fn writeable_borrow(&self) -> Option<&str> {
346        self.data.message.writeable_borrow()
347    }
348
349    fn writeable_length_hint(&self) -> writeable::LengthHint {
350        self.data.message.writeable_length_hint()
351    }
352}
353
354writeable::impl_display_with_writeable!(FormattedHelloWorld<'_>);
355
356#[cfg(feature = "export")]
357#[test]
358fn test_iter() {
359    use crate::IterableDataProvider;
360    use icu_locale_core::locale;
361
362    assert_eq!(
363        HelloWorldProvider.iter_ids().unwrap(),
364        BTreeSet::from_iter([
365            DataIdentifierCow::from_locale(locale!("bn").into()),
366            DataIdentifierCow::from_locale(locale!("cs").into()),
367            DataIdentifierCow::from_locale(locale!("de").into()),
368            DataIdentifierCow::from_locale(locale!("de-AT").into()),
369            DataIdentifierCow::from_locale(locale!("el").into()),
370            DataIdentifierCow::from_locale(locale!("en").into()),
371            DataIdentifierCow::from_locale(locale!("en-001").into()),
372            DataIdentifierCow::from_locale(locale!("en-002").into()),
373            DataIdentifierCow::from_locale(locale!("en-019").into()),
374            DataIdentifierCow::from_locale(locale!("en-142").into()),
375            DataIdentifierCow::from_locale(locale!("en-GB").into()),
376            DataIdentifierCow::from_locale(locale!("en-GB-u-sd-gbeng").into()),
377            DataIdentifierCow::from_borrowed_and_owned(
378                DataMarkerAttributes::from_str_or_panic("reverse"),
379                locale!("en").into()
380            ),
381            DataIdentifierCow::from_locale(locale!("eo").into()),
382            DataIdentifierCow::from_locale(locale!("fa").into()),
383            DataIdentifierCow::from_locale(locale!("fi").into()),
384            DataIdentifierCow::from_locale(locale!("is").into()),
385            DataIdentifierCow::from_locale(locale!("ja").into()),
386            DataIdentifierCow::from_borrowed_and_owned(
387                DataMarkerAttributes::from_str_or_panic("reverse"),
388                locale!("ja").into()
389            ),
390            DataIdentifierCow::from_locale(locale!("la").into()),
391            DataIdentifierCow::from_locale(locale!("pt").into()),
392            DataIdentifierCow::from_locale(locale!("ro").into()),
393            DataIdentifierCow::from_locale(locale!("ru").into()),
394            DataIdentifierCow::from_locale(locale!("sr").into()),
395            DataIdentifierCow::from_locale(locale!("sr-Latn").into()),
396            DataIdentifierCow::from_locale(locale!("vi").into()),
397            DataIdentifierCow::from_locale(locale!("zh").into()),
398        ])
399    );
400}