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