Skip to main content

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}