// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). use super::*; use crate::helpers::result_is_err_missing_data_options; /// A data provider wrapper that performs locale fallback. This enables arbitrary locales to be /// handled at runtime. /// /// # Examples /// /// ``` /// use icu_locid::locale; /// use icu_provider::prelude::*; /// use icu_provider::hello_world::*; /// use icu_provider_adapters::fallback::LocaleFallbackProvider; /// /// let provider = icu_testdata::unstable_no_fallback(); /// /// let req = DataRequest { /// locale: &locale!("ja-JP").into(), /// metadata: Default::default(), /// }; /// /// // The provider does not have data for "ja-JP": /// DataProvider::::load(&provider, req).expect_err("No fallback"); /// /// // But if we wrap the provider in a fallback provider... /// let provider = LocaleFallbackProvider::try_new_unstable(provider) /// .expect("Fallback data present"); /// /// // ...then we can load "ja-JP" based on "ja" data /// let response = /// DataProvider::::load(&provider, req).expect("successful with vertical fallback"); /// /// assert_eq!( /// response.metadata.locale.unwrap(), /// locale!("ja").into(), /// ); /// assert_eq!( /// response.payload.unwrap().get().message, /// "こんにちは世界", /// ); /// ``` #[derive(Clone)] pub struct LocaleFallbackProvider

{ inner: P, fallbacker: LocaleFallbacker, } impl

LocaleFallbackProvider

where P: DataProvider + DataProvider + DataProvider, { /// Create a [`LocaleFallbackProvider`] by wrapping another data provider and then loading /// fallback data from it. /// /// If the data provider being wrapped does not contain fallback data, use /// [`LocaleFallbackProvider::new_with_fallbacker`]. pub fn try_new_unstable(provider: P) -> Result { let fallbacker = LocaleFallbacker::try_new_unstable(&provider)?; Ok(Self { inner: provider, fallbacker, }) } } impl

LocaleFallbackProvider

where P: AnyProvider, { /// Create a [`LocaleFallbackProvider`] by wrapping another data provider and then loading /// fallback data from it. /// /// If the data provider being wrapped does not contain fallback data, use /// [`LocaleFallbackProvider::new_with_fallbacker`]. pub fn try_new_with_any_provider(provider: P) -> Result { let fallbacker = LocaleFallbacker::try_new_with_any_provider(&provider)?; Ok(Self { inner: provider, fallbacker, }) } } #[cfg(feature = "serde")] impl

LocaleFallbackProvider

where P: BufferProvider, { /// Create a [`LocaleFallbackProvider`] by wrapping another data provider and then loading /// fallback data from it. /// /// If the data provider being wrapped does not contain fallback data, use /// [`LocaleFallbackProvider::new_with_fallbacker`]. pub fn try_new_with_buffer_provider(provider: P) -> Result { let fallbacker = LocaleFallbacker::try_new_with_buffer_provider(&provider)?; Ok(Self { inner: provider, fallbacker, }) } } impl

LocaleFallbackProvider

{ /// Wrap a provider with an arbitrary fallback engine. /// /// This relaxes the requirement that the wrapped provider contains its own fallback data. /// /// # Examples /// /// ``` /// use icu_locid::locale; /// use icu_provider::hello_world::*; /// use icu_provider::prelude::*; /// use icu_provider_adapters::fallback::{ /// LocaleFallbackProvider, LocaleFallbacker, /// }; /// /// let provider = HelloWorldProvider; /// /// let req = DataRequest { /// locale: &locale!("de-CH").into(), /// metadata: Default::default(), /// }; /// /// // There is no "de-CH" data in the `HelloWorldProvider` /// DataProvider::::load(&provider, req) /// .expect_err("No data for de-CH"); /// /// // `HelloWorldProvider` does not contain fallback data, /// // but we can fetch it from `icu_testdata`, and then /// // use it to create the fallbacking data provider. /// let fallbacker = /// LocaleFallbacker::try_new_unstable(&icu_testdata::unstable()) /// .expect("Fallback data present"); /// let provider = /// LocaleFallbackProvider::new_with_fallbacker(provider, fallbacker); /// /// // Now we can load the "de-CH" data via fallback to "de". /// let german_hello_world: DataPayload = provider /// .load(req) /// .expect("Loading should succeed") /// .take_payload() /// .expect("Data should be present"); /// /// assert_eq!("Hallo Welt", german_hello_world.get().message); /// ``` pub fn new_with_fallbacker(provider: P, fallbacker: LocaleFallbacker) -> Self { Self { inner: provider, fallbacker, } } /// Returns a reference to the inner provider, bypassing fallback. pub fn inner(&self) -> &P { &self.inner } /// Returns a mutable reference to the inner provider. pub fn inner_mut(&mut self) -> &mut P { &mut self.inner } /// Returns ownership of the inner provider to the caller. pub fn into_inner(self) -> P { self.inner } /// Run the fallback algorithm with the data request using the inner data provider. /// Internal function; external clients should use one of the trait impls below. /// /// Function arguments: /// /// - F1 should perform a data load for a single DataRequest and return the result of it /// - F2 should map from the provider-specific response type to DataResponseMetadata fn run_fallback( &self, key: DataKey, base_req: DataRequest, mut f1: F1, mut f2: F2, ) -> Result where F1: FnMut(DataRequest) -> Result, F2: FnMut(&mut R) -> &mut DataResponseMetadata, { let key_fallbacker = self.fallbacker.for_key(key); let mut fallback_iterator = key_fallbacker.fallback_for(base_req.locale.clone()); loop { let result = f1(DataRequest { locale: fallback_iterator.get(), metadata: Default::default(), }); if !result_is_err_missing_data_options(&result) { return result .map(|mut res| { f2(&mut res).locale = Some(fallback_iterator.take()); res }) // Log the original request rather than the fallback request .map_err(|e| e.with_req(key, base_req)); } // If we just checked und, break out of the loop. if fallback_iterator.get().is_empty() { break; } fallback_iterator.step(); } Err(DataErrorKind::MissingLocale.with_req(key, base_req)) } } impl

AnyProvider for LocaleFallbackProvider

where P: AnyProvider, { fn load_any(&self, key: DataKey, base_req: DataRequest) -> Result { self.run_fallback( key, base_req, |req| self.inner.load_any(key, req), |res| &mut res.metadata, ) } } impl

BufferProvider for LocaleFallbackProvider

where P: BufferProvider, { fn load_buffer( &self, key: DataKey, base_req: DataRequest, ) -> Result, DataError> { self.run_fallback( key, base_req, |req| self.inner.load_buffer(key, req), |res| &mut res.metadata, ) } } impl DynamicDataProvider for LocaleFallbackProvider

where P: DynamicDataProvider, M: DataMarker, { fn load_data(&self, key: DataKey, base_req: DataRequest) -> Result, DataError> { self.run_fallback( key, base_req, |req| self.inner.load_data(key, req), |res| &mut res.metadata, ) } } impl DataProvider for LocaleFallbackProvider

where P: DataProvider, M: KeyedDataMarker, { fn load(&self, base_req: DataRequest) -> Result, DataError> { self.run_fallback( M::KEY, base_req, |req| self.inner.load(req), |res| &mut res.metadata, ) } }