Skip to main content

icu_provider_adapters/fork/
predicates.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//! Collection of predicate traits and functions for forking providers.
6
7use icu_provider::prelude::*;
8
9/// The predicate trait used by [`ForkByErrorProvider`].
10///
11/// [`ForkByErrorProvider`]: super::ForkByErrorProvider
12pub trait ForkByErrorPredicate {
13    /// The error to return if there are zero providers.
14    const UNIT_ERROR: DataErrorKind = DataErrorKind::MarkerNotFound;
15
16    /// This function is called when a data request fails and there are additional providers
17    /// that could possibly fulfill the request.
18    ///
19    /// Arguments:
20    ///
21    /// - `&self` = Reference to the struct implementing the trait (for data capture)
22    /// - `marker` = The [`DataMarkerInfo`] associated with the request
23    /// - `req` = The [`DataRequest`]. This may be `None` if there is no request, such as
24    ///   inside [`IterableDynamicDataProvider`].
25    /// - `err` = The error that occurred.
26    ///
27    /// Return value:
28    ///
29    /// - `true` to discard the error and attempt the request with the next provider.
30    /// - `false` to return the error and not perform any additional requests.
31    fn test(&self, marker: DataMarkerInfo, req: Option<DataRequest>, err: DataError) -> bool;
32}
33
34/// A predicate that allows forking providers to search for a provider that supports a
35/// particular data marker.
36///
37/// This is normally used implicitly by [`ForkByMarkerProvider`].
38///
39/// [`ForkByMarkerProvider`]: super::ForkByMarkerProvider
40#[derive(Debug, PartialEq, Eq)]
41#[non_exhaustive] // Not intended to be constructed
42pub struct MarkerNotFoundPredicate;
43
44impl ForkByErrorPredicate for MarkerNotFoundPredicate {
45    const UNIT_ERROR: DataErrorKind = DataErrorKind::MarkerNotFound;
46
47    #[inline]
48    fn test(&self, _: DataMarkerInfo, _: Option<DataRequest>, err: DataError) -> bool {
49        matches!(
50            err,
51            DataError {
52                kind: DataErrorKind::MarkerNotFound,
53                ..
54            }
55        )
56    }
57}
58
59/// A predicate that allows forking providers to search for a provider that supports a
60/// particular locale, based on whether it returns [`DataErrorKind::IdentifierNotFound`].
61///
62/// # Examples
63///
64/// ```
65/// use icu_provider_adapters::fork::ForkByErrorProvider;
66/// use icu_provider_adapters::fork::predicates::IdentifierNotFoundPredicate;
67/// use icu_provider::prelude::*;
68/// use icu_provider::hello_world::*;
69/// use icu_locale::langid;
70///
71/// struct SingleLocaleProvider(DataLocale);
72/// impl DataProvider<HelloWorldV1> for SingleLocaleProvider {
73///     fn load(&self, req: DataRequest) -> Result<DataResponse<HelloWorldV1>, DataError> {
74///         if *req.id.locale != self.0 {
75///             return Err(DataErrorKind::IdentifierNotFound.with_req(HelloWorldV1::INFO, req));
76///         }
77///         HelloWorldProvider.load(req)
78///     }
79/// }
80///
81/// let provider_de = SingleLocaleProvider(langid!("de").into());
82/// let provider_ro = SingleLocaleProvider(langid!("ro").into());
83///
84/// // Create the forking provider:
85/// let provider = ForkByErrorProvider::new_with_predicate(
86///     provider_de,
87///     provider_ro,
88///     IdentifierNotFoundPredicate
89/// );
90///
91/// // Test that we can load both "de" and "ro" data:
92///
93/// let german_hello_world: DataResponse<HelloWorldV1> = provider
94///     .load(DataRequest {
95///         id: DataIdentifierBorrowed::for_locale(&langid!("de").into()),
96///         ..Default::default()
97///     })
98///     .expect("Loading should succeed");
99///
100/// assert_eq!("Hallo Welt", german_hello_world.payload.get().message);
101///
102/// let romanian_hello_world: DataResponse<HelloWorldV1> = provider
103///     .load(DataRequest {
104///         id: DataIdentifierBorrowed::for_locale(&langid!("ro").into()),
105///         ..Default::default()
106///     })
107///     .expect("Loading should succeed");
108///
109/// assert_eq!("Salut, lume", romanian_hello_world.payload.get().message);
110///
111/// // We should not be able to load "en" data because it is not in either provider:
112///
113/// DataProvider::<HelloWorldV1>::load(
114///     &provider,
115///     DataRequest {
116///         id: DataIdentifierBorrowed::for_locale(&langid!("en").into()),
117///         ..Default::default()
118///     }
119/// )
120/// .expect_err("No English data");
121/// ```
122#[derive(Debug, PartialEq, Eq)]
123#[allow(clippy::exhaustive_structs)] // empty type
124pub struct IdentifierNotFoundPredicate;
125
126impl ForkByErrorPredicate for IdentifierNotFoundPredicate {
127    const UNIT_ERROR: DataErrorKind = DataErrorKind::IdentifierNotFound;
128
129    #[inline]
130    fn test(&self, _: DataMarkerInfo, _: Option<DataRequest>, err: DataError) -> bool {
131        Err::<(), _>(err).allow_identifier_not_found().is_ok()
132    }
133}