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 //! Providers that combine multiple other providers. 6 //! 7 //! # Types of Forking Providers 8 //! 9 //! ## Marker-Based 10 //! 11 //! To fork between providers that support different data markers, see: 12 //! 13 //! - [`ForkByMarkerProvider`] 14 //! - [`MultiForkByMarkerProvider`] 15 //! 16 //! ## Locale-Based 17 //! 18 //! To fork between providers that support different locales, see: 19 //! 20 //! - [`ForkByErrorProvider`]`<`[`IdentiferNotFoundPredicate`]`>` 21 //! - [`MultiForkByErrorProvider`]`<`[`IdentiferNotFoundPredicate`]`>` 22 //! 23 //! [`IdentiferNotFoundPredicate`]: predicates::IdentifierNotFoundPredicate 24 //! 25 //! # Examples 26 //! 27 //! See: 28 //! 29 //! - [`ForkByMarkerProvider`] 30 //! - [`MultiForkByMarkerProvider`] 31 //! - [`IdentiferNotFoundPredicate`] 32 33 use alloc::vec::Vec; 34 35 mod by_error; 36 37 pub mod predicates; 38 39 pub use by_error::ForkByErrorProvider; 40 pub use by_error::MultiForkByErrorProvider; 41 42 use predicates::ForkByErrorPredicate; 43 use predicates::MarkerNotFoundPredicate; 44 45 /// Create a provider that returns data from one of two child providers based on the marker. 46 /// 47 /// The result of the first provider that supports a particular [`DataMarkerInfo`] will be returned, 48 /// even if the request failed for other reasons (such as an unsupported language). Therefore, 49 /// you should add child providers that support disjoint sets of markers. 50 /// 51 /// [`ForkByMarkerProvider`] does not support forking between [`DataProvider`]s. However, it 52 /// supports forking between [`BufferProvider`], and [`DynamicDataProvider`]. 53 /// 54 /// # Examples 55 /// 56 /// Normal usage: 57 /// 58 /// ``` 59 /// use icu_locale::langid; 60 /// use icu_provider::hello_world::*; 61 /// use icu_provider::prelude::*; 62 /// use icu_provider_adapters::fork::ForkByMarkerProvider; 63 /// 64 /// struct DummyBufferProvider; 65 /// impl DynamicDataProvider<BufferMarker> for DummyBufferProvider { 66 /// fn load_data( 67 /// &self, 68 /// marker: DataMarkerInfo, 69 /// req: DataRequest, 70 /// ) -> Result<DataResponse<BufferMarker>, DataError> { 71 /// Err(DataErrorKind::MarkerNotFound.with_req(marker, req)) 72 /// } 73 /// } 74 /// 75 /// let forking_provider = ForkByMarkerProvider::new( 76 /// DummyBufferProvider, 77 /// HelloWorldProvider.into_json_provider(), 78 /// ); 79 /// 80 /// let provider = forking_provider.as_deserializing(); 81 /// 82 /// let german_hello_world: DataResponse<HelloWorldV1> = provider 83 /// .load(DataRequest { 84 /// id: DataIdentifierBorrowed::for_locale(&langid!("de").into()), 85 /// ..Default::default() 86 /// }) 87 /// .expect("Loading should succeed"); 88 /// 89 /// assert_eq!("Hallo Welt", german_hello_world.payload.get().message); 90 /// ``` 91 /// 92 /// Stops at the first provider supporting a marker, even if the locale is not supported: 93 /// 94 /// ``` 95 /// use icu_locale::{subtags::language, langid}; 96 /// use icu_provider::hello_world::*; 97 /// use icu_provider::prelude::*; 98 /// use icu_provider_adapters::filter::FilterDataProvider; 99 /// use icu_provider_adapters::fork::ForkByMarkerProvider; 100 /// 101 /// let forking_provider = ForkByMarkerProvider::new( 102 /// FilterDataProvider::new( 103 /// HelloWorldProvider.into_json_provider(), 104 /// "Chinese" 105 /// ) 106 /// .with_filter(|id| id.locale.language == language!("zh")), 107 /// FilterDataProvider::new( 108 /// HelloWorldProvider.into_json_provider(), 109 /// "German" 110 /// ) 111 /// .with_filter(|id| id.locale.language == language!("de")), 112 /// ); 113 /// 114 /// let provider: &dyn DataProvider<HelloWorldV1> = 115 /// &forking_provider.as_deserializing(); 116 /// 117 /// // Chinese is the first provider, so this succeeds 118 /// let chinese_hello_world = provider 119 /// .load(DataRequest { 120 /// id: DataIdentifierBorrowed::for_locale(&langid!("zh").into()), 121 /// ..Default::default() 122 /// }) 123 /// .expect("Loading should succeed"); 124 /// 125 /// assert_eq!("你好世界", chinese_hello_world.payload.get().message); 126 /// 127 /// // German is shadowed by Chinese, so this fails 128 /// provider 129 /// .load(DataRequest { 130 /// id: DataIdentifierBorrowed::for_locale(&langid!("de").into()), 131 /// ..Default::default() 132 /// }) 133 /// .expect_err("Should stop at the first provider, even though the second has data"); 134 /// ``` 135 /// 136 /// [`DataMarkerInfo`]: icu_provider::DataMarkerInfo 137 /// [`DataProvider`]: icu_provider::DataProvider 138 /// [`BufferProvider`]: icu_provider::buf::BufferProvider 139 /// [`DynamicDataProvider`]: icu_provider::DynamicDataProvider 140 pub type ForkByMarkerProvider<P0, P1> = ForkByErrorProvider<P0, P1, MarkerNotFoundPredicate>; 141 142 impl<P0, P1> ForkByMarkerProvider<P0, P1> { 143 /// A provider that returns data from one of two child providers based on the marker. 144 /// 145 /// See [`ForkByMarkerProvider`]. new(p0: P0, p1: P1) -> Self146 pub fn new(p0: P0, p1: P1) -> Self { 147 ForkByErrorProvider::new_with_predicate(p0, p1, MarkerNotFoundPredicate) 148 } 149 } 150 151 /// A provider that returns data from the first child provider supporting the marker. 152 /// 153 /// The result of the first provider that supports a particular [`DataMarkerInfo`] will be returned, 154 /// even if the request failed for other reasons (such as an unsupported language). Therefore, 155 /// you should add child providers that support disjoint sets of markers. 156 /// 157 /// [`MultiForkByMarkerProvider`] does not support forking between [`DataProvider`]s. However, it 158 /// supports forking between [`BufferProvider`], and [`DynamicDataProvider`]. 159 /// 160 /// # Examples 161 /// 162 /// ``` 163 /// use icu_locale::{subtags::language, langid}; 164 /// use icu_provider::hello_world::*; 165 /// use icu_provider::prelude::*; 166 /// use icu_provider_adapters::filter::FilterDataProvider; 167 /// use icu_provider_adapters::fork::MultiForkByMarkerProvider; 168 /// 169 /// let forking_provider = MultiForkByMarkerProvider::new( 170 /// vec![ 171 /// FilterDataProvider::new( 172 /// HelloWorldProvider.into_json_provider(), 173 /// "Chinese" 174 /// ) 175 /// .with_filter(|id| id.locale.language == language!("zh")), 176 /// FilterDataProvider::new( 177 /// HelloWorldProvider.into_json_provider(), 178 /// "German" 179 /// ) 180 /// .with_filter(|id| id.locale.language == language!("de")), 181 /// ], 182 /// ); 183 /// 184 /// let provider: &dyn DataProvider<HelloWorldV1> = 185 /// &forking_provider.as_deserializing(); 186 /// 187 /// // Chinese is the first provider, so this succeeds 188 /// let chinese_hello_world = provider 189 /// .load(DataRequest { 190 /// id: DataIdentifierBorrowed::for_locale(&langid!("zh").into()), 191 /// ..Default::default() 192 /// }) 193 /// .expect("Loading should succeed"); 194 /// 195 /// assert_eq!("你好世界", chinese_hello_world.payload.get().message); 196 /// 197 /// // German is shadowed by Chinese, so this fails 198 /// provider 199 /// .load(DataRequest { 200 /// id: DataIdentifierBorrowed::for_locale(&langid!("de").into()), 201 /// ..Default::default() 202 /// }) 203 /// .expect_err("Should stop at the first provider, even though the second has data"); 204 /// ``` 205 /// 206 /// [`DataMarkerInfo`]: icu_provider::DataMarkerInfo 207 /// [`DataProvider`]: icu_provider::DataProvider 208 /// [`BufferProvider`]: icu_provider::buf::BufferProvider 209 /// [`DynamicDataProvider`]: icu_provider::DynamicDataProvider 210 pub type MultiForkByMarkerProvider<P> = MultiForkByErrorProvider<P, MarkerNotFoundPredicate>; 211 212 impl<P> MultiForkByMarkerProvider<P> { 213 /// Create a provider that returns data from the first child provider supporting the marker. 214 /// 215 /// See [`MultiForkByMarkerProvider`]. new(providers: Vec<P>) -> Self216 pub fn new(providers: Vec<P>) -> Self { 217 MultiForkByErrorProvider::new_with_predicate(providers, MarkerNotFoundPredicate) 218 } 219 } 220