// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). use core::marker::PhantomData; use yoke::Yokeable; use crate::prelude::*; /// A data provider that loads data for a specific [`DataMarkerInfo`]. pub trait DataProvider where M: DataMarker, { /// Query the provider for data, returning the result. /// /// Returns [`Ok`] if the request successfully loaded data. If data failed to load, returns an /// Error with more information. fn load(&self, req: DataRequest) -> Result, DataError>; } impl DataProvider for &P where M: DataMarker, P: DataProvider + ?Sized, { #[inline] fn load(&self, req: DataRequest) -> Result, DataError> { (*self).load(req) } } #[cfg(feature = "alloc")] impl DataProvider for alloc::boxed::Box

where M: DataMarker, P: DataProvider + ?Sized, { #[inline] fn load(&self, req: DataRequest) -> Result, DataError> { (**self).load(req) } } #[cfg(feature = "alloc")] impl DataProvider for alloc::rc::Rc

where M: DataMarker, P: DataProvider + ?Sized, { #[inline] fn load(&self, req: DataRequest) -> Result, DataError> { (**self).load(req) } } #[cfg(target_has_atomic = "ptr")] #[cfg(feature = "alloc")] impl DataProvider for alloc::sync::Arc

where M: DataMarker, P: DataProvider + ?Sized, { #[inline] fn load(&self, req: DataRequest) -> Result, DataError> { (**self).load(req) } } /// A data provider that can determine whether it can load a particular data identifier, /// potentially cheaper than actually performing the load. pub trait DryDataProvider: DataProvider { /// This method goes through the motions of [`load`], but only returns the metadata. /// /// If `dry_load` returns an error, [`load`] must return the same error, but /// not vice-versa. Concretely, [`load`] could return deserialization or I/O errors /// that `dry_load` cannot predict. /// /// [`load`]: DataProvider::load fn dry_load(&self, req: DataRequest) -> Result; } impl DryDataProvider for &P where M: DataMarker, P: DryDataProvider + ?Sized, { #[inline] fn dry_load(&self, req: DataRequest) -> Result { (*self).dry_load(req) } } #[cfg(feature = "alloc")] impl DryDataProvider for alloc::boxed::Box

where M: DataMarker, P: DryDataProvider + ?Sized, { #[inline] fn dry_load(&self, req: DataRequest) -> Result { (**self).dry_load(req) } } #[cfg(feature = "alloc")] impl DryDataProvider for alloc::rc::Rc

where M: DataMarker, P: DryDataProvider + ?Sized, { #[inline] fn dry_load(&self, req: DataRequest) -> Result { (**self).dry_load(req) } } #[cfg(target_has_atomic = "ptr")] #[cfg(feature = "alloc")] impl DryDataProvider for alloc::sync::Arc

where M: DataMarker, P: DryDataProvider + ?Sized, { #[inline] fn dry_load(&self, req: DataRequest) -> Result { (**self).dry_load(req) } } /// A [`DataProvider`] that can iterate over all supported [`DataIdentifierCow`]s. /// /// The provider is not allowed to return `Ok` for requests that were not returned by `iter_ids`, /// and must not fail with a [`DataErrorKind::IdentifierNotFound`] for requests that were returned. #[cfg(feature = "alloc")] pub trait IterableDataProvider: DataProvider { /// Returns a set of [`DataIdentifierCow`]. fn iter_ids(&self) -> Result, DataError>; } /// A data provider that loads data for a specific data type. /// /// Unlike [`DataProvider`], there may be multiple markers corresponding to the same data type. pub trait DynamicDataProvider where M: DynamicDataMarker, { /// Query the provider for data, returning the result. /// /// Returns [`Ok`] if the request successfully loaded data. If data failed to load, returns an /// Error with more information. fn load_data( &self, marker: DataMarkerInfo, req: DataRequest, ) -> Result, DataError>; } impl DynamicDataProvider for &P where M: DynamicDataMarker, P: DynamicDataProvider + ?Sized, { #[inline] fn load_data( &self, marker: DataMarkerInfo, req: DataRequest, ) -> Result, DataError> { (*self).load_data(marker, req) } } #[cfg(feature = "alloc")] impl DynamicDataProvider for alloc::boxed::Box

where M: DynamicDataMarker, P: DynamicDataProvider + ?Sized, { #[inline] fn load_data( &self, marker: DataMarkerInfo, req: DataRequest, ) -> Result, DataError> { (**self).load_data(marker, req) } } #[cfg(feature = "alloc")] impl DynamicDataProvider for alloc::rc::Rc

where M: DynamicDataMarker, P: DynamicDataProvider + ?Sized, { #[inline] fn load_data( &self, marker: DataMarkerInfo, req: DataRequest, ) -> Result, DataError> { (**self).load_data(marker, req) } } #[cfg(target_has_atomic = "ptr")] #[cfg(feature = "alloc")] impl DynamicDataProvider for alloc::sync::Arc

where M: DynamicDataMarker, P: DynamicDataProvider + ?Sized, { #[inline] fn load_data( &self, marker: DataMarkerInfo, req: DataRequest, ) -> Result, DataError> { (**self).load_data(marker, req) } } /// A dynanmic data provider that can determine whether it can load a particular data identifier, /// potentially cheaper than actually performing the load. pub trait DynamicDryDataProvider: DynamicDataProvider { /// This method goes through the motions of [`load_data`], but only returns the metadata. /// /// If `dry_load_data` returns an error, [`load_data`] must return the same error, but /// not vice-versa. Concretely, [`load_data`] could return deserialization or I/O errors /// that `dry_load_data` cannot predict. /// /// [`load_data`]: DynamicDataProvider::load_data fn dry_load_data( &self, marker: DataMarkerInfo, req: DataRequest, ) -> Result; } impl DynamicDryDataProvider for &P where M: DynamicDataMarker, P: DynamicDryDataProvider + ?Sized, { #[inline] fn dry_load_data( &self, marker: DataMarkerInfo, req: DataRequest, ) -> Result { (*self).dry_load_data(marker, req) } } #[cfg(feature = "alloc")] impl DynamicDryDataProvider for alloc::boxed::Box

where M: DynamicDataMarker, P: DynamicDryDataProvider + ?Sized, { #[inline] fn dry_load_data( &self, marker: DataMarkerInfo, req: DataRequest, ) -> Result { (**self).dry_load_data(marker, req) } } #[cfg(feature = "alloc")] impl DynamicDryDataProvider for alloc::rc::Rc

where M: DynamicDataMarker, P: DynamicDryDataProvider + ?Sized, { #[inline] fn dry_load_data( &self, marker: DataMarkerInfo, req: DataRequest, ) -> Result { (**self).dry_load_data(marker, req) } } #[cfg(target_has_atomic = "ptr")] #[cfg(feature = "alloc")] impl DynamicDryDataProvider for alloc::sync::Arc

where M: DynamicDataMarker, P: DynamicDryDataProvider + ?Sized, { #[inline] fn dry_load_data( &self, marker: DataMarkerInfo, req: DataRequest, ) -> Result { (**self).dry_load_data(marker, req) } } /// A [`DynamicDataProvider`] that can iterate over all supported [`DataIdentifierCow`]s for a certain marker. /// /// The provider is not allowed to return `Ok` for requests that were not returned by `iter_ids`, /// and must not fail with a [`DataErrorKind::IdentifierNotFound`] for requests that were returned. #[cfg(feature = "alloc")] pub trait IterableDynamicDataProvider: DynamicDataProvider { /// Given a [`DataMarkerInfo`], returns a set of [`DataIdentifierCow`]. fn iter_ids_for_marker( &self, marker: DataMarkerInfo, ) -> Result, DataError>; } #[cfg(feature = "alloc")] impl IterableDynamicDataProvider for alloc::boxed::Box

where M: DynamicDataMarker, P: IterableDynamicDataProvider + ?Sized, { fn iter_ids_for_marker( &self, marker: DataMarkerInfo, ) -> Result, DataError> { (**self).iter_ids_for_marker(marker) } } /// A data provider that loads data for a specific data type. /// /// Unlike [`DataProvider`], the provider is bound to a specific marker ahead of time. /// /// This crate provides [`DataProviderWithMarker`] which implements this trait on a single provider /// with a single marker. However, this trait can also be implemented on providers that fork between /// multiple markers that all return the same data type. For example, it can abstract over many /// calendar systems in the datetime formatter. pub trait BoundDataProvider where M: DynamicDataMarker, { /// Query the provider for data, returning the result. /// /// Returns [`Ok`] if the request successfully loaded data. If data failed to load, returns an /// Error with more information. fn load_bound(&self, req: DataRequest) -> Result, DataError>; /// Returns the [`DataMarkerInfo`] that this provider uses for loading data. fn bound_marker(&self) -> DataMarkerInfo; } impl BoundDataProvider for &P where M: DynamicDataMarker, P: BoundDataProvider + ?Sized, { #[inline] fn load_bound(&self, req: DataRequest) -> Result, DataError> { (*self).load_bound(req) } #[inline] fn bound_marker(&self) -> DataMarkerInfo { (*self).bound_marker() } } #[cfg(feature = "alloc")] impl BoundDataProvider for alloc::boxed::Box

where M: DynamicDataMarker, P: BoundDataProvider + ?Sized, { #[inline] fn load_bound(&self, req: DataRequest) -> Result, DataError> { (**self).load_bound(req) } #[inline] fn bound_marker(&self) -> DataMarkerInfo { (**self).bound_marker() } } #[cfg(feature = "alloc")] impl BoundDataProvider for alloc::rc::Rc

where M: DynamicDataMarker, P: BoundDataProvider + ?Sized, { #[inline] fn load_bound(&self, req: DataRequest) -> Result, DataError> { (**self).load_bound(req) } #[inline] fn bound_marker(&self) -> DataMarkerInfo { (**self).bound_marker() } } #[cfg(target_has_atomic = "ptr")] #[cfg(feature = "alloc")] impl BoundDataProvider for alloc::sync::Arc

where M: DynamicDataMarker, P: BoundDataProvider + ?Sized, { #[inline] fn load_bound(&self, req: DataRequest) -> Result, DataError> { (**self).load_bound(req) } #[inline] fn bound_marker(&self) -> DataMarkerInfo { (**self).bound_marker() } } /// A [`DataProvider`] associated with a specific marker. /// /// Implements [`BoundDataProvider`]. #[derive(Debug)] pub struct DataProviderWithMarker { inner: P, _marker: PhantomData, } impl DataProviderWithMarker where M: DataMarker, P: DataProvider, { /// Creates a [`DataProviderWithMarker`] from a [`DataProvider`] with a [`DataMarker`]. pub const fn new(inner: P) -> Self { Self { inner, _marker: PhantomData, } } } impl BoundDataProvider for DataProviderWithMarker where M: DataMarker, M0: DynamicDataMarker, Y: for<'a> Yokeable<'a>, P: DataProvider, { #[inline] fn load_bound(&self, req: DataRequest) -> Result, DataError> { self.inner.load(req).map(DataResponse::cast) } #[inline] fn bound_marker(&self) -> DataMarkerInfo { M::INFO } } #[cfg(test)] mod test { use super::*; use crate::hello_world::*; use alloc::borrow::Cow; use alloc::string::String; use core::fmt::Debug; use serde::{Deserialize, Serialize}; // This tests DataProvider borrow semantics with a dummy data provider based on a // JSON string. It also exercises most of the data provider code paths. /// A data struct serialization-compatible with HelloWorld used for testing mismatched types #[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, yoke::Yokeable)] pub struct HelloAlt { message: String, } data_marker!(HelloAltMarkerV1, HelloAlt); #[derive(Deserialize, Debug, Clone, Default, PartialEq)] struct HelloCombined<'data> { #[serde(borrow)] pub hello_v1: HelloWorld<'data>, pub hello_alt: HelloAlt, } /// A DataProvider that owns its data, returning an Rc-variant DataPayload. /// Supports only key::HELLO_WORLD_V1. Uses `impl_dynamic_data_provider!()`. #[derive(Debug)] struct DataWarehouse { hello_v1: HelloWorld<'static>, hello_alt: HelloAlt, } impl DataProvider for DataWarehouse { fn load(&self, _: DataRequest) -> Result, DataError> { Ok(DataResponse { metadata: DataResponseMetadata::default(), payload: DataPayload::from_owned(self.hello_v1.clone()), }) } } /// A DataProvider that supports both key::HELLO_WORLD_V1 and HELLO_ALT. #[derive(Debug)] struct DataProvider2 { data: DataWarehouse, } impl From for DataProvider2 { fn from(warehouse: DataWarehouse) -> Self { DataProvider2 { data: warehouse } } } impl DataProvider for DataProvider2 { fn load(&self, _: DataRequest) -> Result, DataError> { Ok(DataResponse { metadata: DataResponseMetadata::default(), payload: DataPayload::from_owned(self.data.hello_v1.clone()), }) } } impl DataProvider for DataProvider2 { fn load(&self, _: DataRequest) -> Result, DataError> { Ok(DataResponse { metadata: DataResponseMetadata::default(), payload: DataPayload::from_owned(self.data.hello_alt.clone()), }) } } const DATA: &str = r#"{ "hello_v1": { "message": "Hello " }, "hello_alt": { "message": "Hello Alt" } }"#; fn get_warehouse(data: &'static str) -> DataWarehouse { let data: HelloCombined = serde_json::from_str(data).expect("Well-formed data"); DataWarehouse { hello_v1: data.hello_v1, hello_alt: data.hello_alt, } } fn get_payload_v1 + ?Sized>( provider: &P, ) -> Result, DataError> { provider.load(Default::default()).map(|r| r.payload) } fn get_payload_alt + ?Sized>( provider: &P, ) -> Result, DataError> { provider.load(Default::default()).map(|r| r.payload) } #[test] fn test_warehouse_owned() { let warehouse = get_warehouse(DATA); let hello_data = get_payload_v1(&warehouse).unwrap(); assert!(matches!( hello_data.get(), HelloWorld { message: Cow::Borrowed(_), } )); } #[test] fn test_warehouse_owned_dyn_generic() { let warehouse = get_warehouse(DATA); let hello_data = get_payload_v1(&warehouse as &dyn DataProvider).unwrap(); assert!(matches!( hello_data.get(), HelloWorld { message: Cow::Borrowed(_), } )); } #[test] fn test_provider2() { let warehouse = get_warehouse(DATA); let provider = DataProvider2::from(warehouse); let hello_data = get_payload_v1(&provider).unwrap(); assert!(matches!( hello_data.get(), HelloWorld { message: Cow::Borrowed(_), } )); } #[test] fn test_provider2_dyn_generic() { let warehouse = get_warehouse(DATA); let provider = DataProvider2::from(warehouse); let hello_data = get_payload_v1(&provider as &dyn DataProvider).unwrap(); assert!(matches!( hello_data.get(), HelloWorld { message: Cow::Borrowed(_), } )); } #[test] fn test_provider2_dyn_generic_alt() { let warehouse = get_warehouse(DATA); let provider = DataProvider2::from(warehouse); let hello_data = get_payload_alt(&provider as &dyn DataProvider).unwrap(); assert!(matches!(hello_data.get(), HelloAlt { .. })); } fn check_v1_v2

(d: &P) where P: DataProvider + DataProvider + ?Sized, { let v1: DataPayload = d.load(Default::default()).unwrap().payload; let v2: DataPayload = d.load(Default::default()).unwrap().payload; if v1.get().message == v2.get().message { panic!() } } #[test] fn test_v1_v2_generic() { let warehouse = get_warehouse(DATA); let provider = DataProvider2::from(warehouse); check_v1_v2(&provider); } }