| 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::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)] |
| 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-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 | |
| 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: &'static 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::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)] |
| 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 | 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 | } |
| 363 | |