icu_provider_adapters/fork/mod.rs
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
33use alloc::vec::Vec;
34
35mod by_error;
36
37pub mod predicates;
38
39pub use by_error::ForkByErrorProvider;
40pub use by_error::MultiForkByErrorProvider;
41
42use predicates::ForkByErrorPredicate;
43use 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
140pub type ForkByMarkerProvider<P0, P1> = ForkByErrorProvider<P0, P1, MarkerNotFoundPredicate>;
141
142impl<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`].
146 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
210pub type MultiForkByMarkerProvider<P> = MultiForkByErrorProvider<P, MarkerNotFoundPredicate>;
211
212impl<P> MultiForkByMarkerProvider<P> {
213 /// Create a provider that returns data from the first child provider supporting the marker.
214 ///
215 /// See [`MultiForkByMarkerProvider`].
216 pub fn new(providers: Vec<P>) -> Self {
217 MultiForkByErrorProvider::new_with_predicate(providers, MarkerNotFoundPredicate)
218 }
219}