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
33use alloc::vec::Vec;
34
35mod by_error;
36
37pub mod predicates;
38
39#[macro_use]
40mod macros;
41
42pub use by_error::ForkByErrorProvider;
43pub use by_error::MultiForkByErrorProvider;
44
45use predicates::ForkByErrorPredicate;
46use 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
146pub type ForkByKeyProvider<P0, P1> = ForkByErrorProvider<P0, P1, MissingDataKeyPredicate>;
147
148impl<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
217pub type MultiForkByKeyProvider<P> = MultiForkByErrorProvider<P, MissingDataKeyPredicate>;
218
219impl<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