// 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 crate::error::DataError; use crate::key::DataKey; use crate::marker::{DataMarker, KeyedDataMarker}; use crate::request::DataRequest; use crate::response::DataResponse; /// A data provider that loads data for a specific data type. /// /// Unlike [`DataProvider`], there may be multiple keys corresponding to the same data type. /// This is often the case when returning `dyn` trait objects such as [`AnyMarker`]. /// /// [`AnyMarker`]: crate::any::AnyMarker pub trait DynamicDataProvider 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_data(&self, key: DataKey, req: DataRequest) -> Result, DataError>; } /// A data provider that loads data for a specific [`DataKey`]. pub trait DataProvider where M: KeyedDataMarker, { /// 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 DynamicDataProvider for alloc::boxed::Box

where M: DataMarker, P: DynamicDataProvider + ?Sized, { fn load_data(&self, key: DataKey, req: DataRequest) -> Result, DataError> { (**self).load_data(key, req) } } #[cfg(test)] mod test { use super::*; use crate::hello_world::*; use crate::prelude::*; 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. /// Key for HelloAlt, used for testing mismatched types const HELLO_ALT_KEY: DataKey = crate::data_key!("core/helloalt@1"); /// A data struct serialization-compatible with HelloWorldV1 used for testing mismatched types #[derive( Serialize, Deserialize, Debug, Clone, Default, PartialEq, yoke::Yokeable, zerofrom::ZeroFrom, )] struct HelloAlt { #[zerofrom(clone)] message: String, } /// Marker type for [`HelloAlt`]. struct HelloAltMarker {} impl DataMarker for HelloAltMarker { type Yokeable = HelloAlt; } impl KeyedDataMarker for HelloAltMarker { const KEY: DataKey = HELLO_ALT_KEY; } #[derive(Deserialize, Debug, Clone, Default, PartialEq)] struct HelloCombined<'data> { #[serde(borrow)] pub hello_v1: HelloWorldV1<'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: HelloWorldV1<'static>, hello_alt: HelloAlt, } impl DataProvider for DataWarehouse { fn load(&self, _: DataRequest) -> Result, DataError> { Ok(DataResponse { metadata: DataResponseMetadata::default(), payload: Some(DataPayload::from_owned(self.hello_v1.clone())), }) } } crate::impl_dynamic_data_provider!(DataWarehouse, [HelloWorldV1Marker,], AnyMarker); /// 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: Some(DataPayload::from_owned(self.data.hello_v1.clone())), }) } } impl DataProvider for DataProvider2 { fn load(&self, _: DataRequest) -> Result, DataError> { Ok(DataResponse { metadata: DataResponseMetadata::default(), payload: Some(DataPayload::from_owned(self.data.hello_alt.clone())), }) } } crate::impl_dynamic_data_provider!( DataProvider2, [HelloWorldV1Marker, HelloAltMarker,], AnyMarker ); const DATA: &str = r#"{ "hello_v1": { "message": "Hello V1" }, "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())?.take_payload() } fn get_payload_alt + ?Sized>( provider: &P, ) -> Result, DataError> { provider.load(Default::default())?.take_payload() } #[test] fn test_warehouse_owned() { let warehouse = get_warehouse(DATA); let hello_data = get_payload_v1(&warehouse).unwrap(); assert!(matches!( hello_data.get(), HelloWorldV1 { message: Cow::Borrowed(_), } )); } #[test] fn test_warehouse_owned_dyn_erased() { let warehouse = get_warehouse(DATA); let hello_data = get_payload_v1(&warehouse.as_any_provider().as_downcasting()).unwrap(); assert!(matches!( hello_data.get(), HelloWorldV1 { 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(), HelloWorldV1 { message: Cow::Borrowed(_), } )); } #[test] fn test_warehouse_owned_dyn_erased_alt() { let warehouse = get_warehouse(DATA); let response = get_payload_alt(&warehouse.as_any_provider().as_downcasting()); assert!(matches!( response, Err(DataError { kind: DataErrorKind::MissingDataKey, .. }) )); } #[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(), HelloWorldV1 { message: Cow::Borrowed(_), } )); } #[test] fn test_provider2_dyn_erased() { let warehouse = get_warehouse(DATA); let provider = DataProvider2::from(warehouse); let hello_data = get_payload_v1(&provider.as_any_provider().as_downcasting()).unwrap(); assert!(matches!( hello_data.get(), HelloWorldV1 { message: Cow::Borrowed(_), } )); } #[test] fn test_provider2_dyn_erased_alt() { let warehouse = get_warehouse(DATA); let provider = DataProvider2::from(warehouse); let hello_data = get_payload_alt(&provider.as_any_provider().as_downcasting()).unwrap(); assert!(matches!(hello_data.get(), HelloAlt { .. })); } #[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(), HelloWorldV1 { 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 { .. })); } #[test] fn test_mismatched_types() { let warehouse = get_warehouse(DATA); let provider = DataProvider2::from(warehouse); // Request is for v2, but type argument is for v1 let response: Result, DataError> = AnyProvider::load_any( &provider.as_any_provider(), HELLO_ALT_KEY, Default::default(), ) .unwrap() .downcast(); assert!(matches!( response, Err(DataError { kind: DataErrorKind::MismatchedType(_), .. }) )); } fn check_v1_v2

(d: &P) where P: DataProvider + DataProvider + ?Sized, { let v1: DataPayload = d.load(Default::default()).unwrap().take_payload().unwrap(); let v2: DataPayload = d.load(Default::default()).unwrap().take_payload().unwrap(); 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); } #[test] fn test_v1_v2_dyn_erased() { let warehouse = get_warehouse(DATA); let provider = DataProvider2::from(warehouse); check_v1_v2(&provider.as_any_provider().as_downcasting()); } }