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 7 use icu_provider::prelude::*; 8 9 /// The predicate trait used by [`ForkByErrorProvider`]. 10 /// 11 /// [`ForkByErrorProvider`]: super::ForkByErrorProvider 12 pub 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. test(&self, marker: DataMarkerInfo, req: Option<DataRequest>, err: DataError) -> bool31 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 42 pub struct MarkerNotFoundPredicate; 43 44 impl ForkByErrorPredicate for MarkerNotFoundPredicate { 45 const UNIT_ERROR: DataErrorKind = DataErrorKind::MarkerNotFound; 46 47 #[inline] test(&self, _: DataMarkerInfo, _: Option<DataRequest>, err: DataError) -> bool48 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 124 pub struct IdentifierNotFoundPredicate; 125 126 impl ForkByErrorPredicate for IdentifierNotFoundPredicate { 127 const UNIT_ERROR: DataErrorKind = DataErrorKind::IdentifierNotFound; 128 129 #[inline] test(&self, _: DataMarkerInfo, _: Option<DataRequest>, err: DataError) -> bool130 fn test(&self, _: DataMarkerInfo, _: Option<DataRequest>, err: DataError) -> bool { 131 Err::<(), _>(err).allow_identifier_not_found().is_ok() 132 } 133 } 134