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
5use crate::error::DataError;
6use crate::key::DataKey;
7use crate::marker::{DataMarker, KeyedDataMarker};
8use crate::request::DataRequest;
9use crate::response::DataResponse;
10
11/// A data provider that loads data for a specific data type.
12///
13/// Unlike [`DataProvider`], there may be multiple keys corresponding to the same data type.
14/// This is often the case when returning `dyn` trait objects such as [`AnyMarker`].
15///
16/// [`AnyMarker`]: crate::any::AnyMarker
17pub trait DynamicDataProvider<M>
18where
19 M: DataMarker,
20{
21 /// Query the provider for data, returning the result.
22 ///
23 /// Returns [`Ok`] if the request successfully loaded data. If data failed to load, returns an
24 /// Error with more information.
25 fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError>;
26}
27
28/// A data provider that loads data for a specific [`DataKey`].
29pub trait DataProvider<M>
30where
31 M: KeyedDataMarker,
32{
33 /// Query the provider for data, returning the result.
34 ///
35 /// Returns [`Ok`] if the request successfully loaded data. If data failed to load, returns an
36 /// Error with more information.
37 fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError>;
38}
39
40impl<M, P> DynamicDataProvider<M> for alloc::boxed::Box<P>
41where
42 M: DataMarker,
43 P: DynamicDataProvider<M> + ?Sized,
44{
45 fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError> {
46 (**self).load_data(key, req)
47 }
48}
49
50#[cfg(test)]
51mod test {
52
53 use super::*;
54 use crate::hello_world::*;
55 use crate::prelude::*;
56 use alloc::borrow::Cow;
57 use alloc::string::String;
58 use core::fmt::Debug;
59 use serde::{Deserialize, Serialize};
60
61 // This tests DataProvider borrow semantics with a dummy data provider based on a
62 // JSON string. It also exercises most of the data provider code paths.
63
64 /// Key for HelloAlt, used for testing mismatched types
65 const HELLO_ALT_KEY: DataKey = crate::data_key!("core/helloalt@1");
66
67 /// A data struct serialization-compatible with HelloWorldV1 used for testing mismatched types
68 #[derive(
69 Serialize, Deserialize, Debug, Clone, Default, PartialEq, yoke::Yokeable, zerofrom::ZeroFrom,
70 )]
71 struct HelloAlt {
72 #[zerofrom(clone)]
73 message: String,
74 }
75
76 /// Marker type for [`HelloAlt`].
77 struct HelloAltMarker {}
78
79 impl DataMarker for HelloAltMarker {
80 type Yokeable = HelloAlt;
81 }
82
83 impl KeyedDataMarker for HelloAltMarker {
84 const KEY: DataKey = HELLO_ALT_KEY;
85 }
86
87 #[derive(Deserialize, Debug, Clone, Default, PartialEq)]
88 struct HelloCombined<'data> {
89 #[serde(borrow)]
90 pub hello_v1: HelloWorldV1<'data>,
91 pub hello_alt: HelloAlt,
92 }
93
94 /// A DataProvider that owns its data, returning an Rc-variant DataPayload.
95 /// Supports only key::HELLO_WORLD_V1. Uses `impl_dynamic_data_provider!()`.
96 #[derive(Debug)]
97 struct DataWarehouse {
98 hello_v1: HelloWorldV1<'static>,
99 hello_alt: HelloAlt,
100 }
101
102 impl DataProvider<HelloWorldV1Marker> for DataWarehouse {
103 fn load(&self, _: DataRequest) -> Result<DataResponse<HelloWorldV1Marker>, DataError> {
104 Ok(DataResponse {
105 metadata: DataResponseMetadata::default(),
106 payload: Some(DataPayload::from_owned(self.hello_v1.clone())),
107 })
108 }
109 }
110
111 crate::impl_dynamic_data_provider!(DataWarehouse, [HelloWorldV1Marker,], AnyMarker);
112
113 /// A DataProvider that supports both key::HELLO_WORLD_V1 and HELLO_ALT.
114 #[derive(Debug)]
115 struct DataProvider2 {
116 data: DataWarehouse,
117 }
118
119 impl From<DataWarehouse> for DataProvider2 {
120 fn from(warehouse: DataWarehouse) -> Self {
121 DataProvider2 { data: warehouse }
122 }
123 }
124
125 impl DataProvider<HelloWorldV1Marker> for DataProvider2 {
126 fn load(&self, _: DataRequest) -> Result<DataResponse<HelloWorldV1Marker>, DataError> {
127 Ok(DataResponse {
128 metadata: DataResponseMetadata::default(),
129 payload: Some(DataPayload::from_owned(self.data.hello_v1.clone())),
130 })
131 }
132 }
133
134 impl DataProvider<HelloAltMarker> for DataProvider2 {
135 fn load(&self, _: DataRequest) -> Result<DataResponse<HelloAltMarker>, DataError> {
136 Ok(DataResponse {
137 metadata: DataResponseMetadata::default(),
138 payload: Some(DataPayload::from_owned(self.data.hello_alt.clone())),
139 })
140 }
141 }
142
143 crate::impl_dynamic_data_provider!(
144 DataProvider2,
145 [HelloWorldV1Marker, HelloAltMarker,],
146 AnyMarker
147 );
148
149 const DATA: &str = r#"{
150 "hello_v1": {
151 "message": "Hello V1"
152 },
153 "hello_alt": {
154 "message": "Hello Alt"
155 }
156 }"#;
157
158 fn get_warehouse(data: &'static str) -> DataWarehouse {
159 let data: HelloCombined = serde_json::from_str(data).expect("Well-formed data");
160 DataWarehouse {
161 hello_v1: data.hello_v1,
162 hello_alt: data.hello_alt,
163 }
164 }
165
166 fn get_payload_v1<P: DataProvider<HelloWorldV1Marker> + ?Sized>(
167 provider: &P,
168 ) -> Result<DataPayload<HelloWorldV1Marker>, DataError> {
169 provider.load(Default::default())?.take_payload()
170 }
171
172 fn get_payload_alt<P: DataProvider<HelloAltMarker> + ?Sized>(
173 provider: &P,
174 ) -> Result<DataPayload<HelloAltMarker>, DataError> {
175 provider.load(Default::default())?.take_payload()
176 }
177
178 #[test]
179 fn test_warehouse_owned() {
180 let warehouse = get_warehouse(DATA);
181 let hello_data = get_payload_v1(&warehouse).unwrap();
182 assert!(matches!(
183 hello_data.get(),
184 HelloWorldV1 {
185 message: Cow::Borrowed(_),
186 }
187 ));
188 }
189
190 #[test]
191 fn test_warehouse_owned_dyn_erased() {
192 let warehouse = get_warehouse(DATA);
193 let hello_data = get_payload_v1(&warehouse.as_any_provider().as_downcasting()).unwrap();
194 assert!(matches!(
195 hello_data.get(),
196 HelloWorldV1 {
197 message: Cow::Borrowed(_),
198 }
199 ));
200 }
201
202 #[test]
203 fn test_warehouse_owned_dyn_generic() {
204 let warehouse = get_warehouse(DATA);
205 let hello_data =
206 get_payload_v1(&warehouse as &dyn DataProvider<HelloWorldV1Marker>).unwrap();
207 assert!(matches!(
208 hello_data.get(),
209 HelloWorldV1 {
210 message: Cow::Borrowed(_),
211 }
212 ));
213 }
214
215 #[test]
216 fn test_warehouse_owned_dyn_erased_alt() {
217 let warehouse = get_warehouse(DATA);
218 let response = get_payload_alt(&warehouse.as_any_provider().as_downcasting());
219 assert!(matches!(
220 response,
221 Err(DataError {
222 kind: DataErrorKind::MissingDataKey,
223 ..
224 })
225 ));
226 }
227
228 #[test]
229 fn test_provider2() {
230 let warehouse = get_warehouse(DATA);
231 let provider = DataProvider2::from(warehouse);
232 let hello_data = get_payload_v1(&provider).unwrap();
233 assert!(matches!(
234 hello_data.get(),
235 HelloWorldV1 {
236 message: Cow::Borrowed(_),
237 }
238 ));
239 }
240
241 #[test]
242 fn test_provider2_dyn_erased() {
243 let warehouse = get_warehouse(DATA);
244 let provider = DataProvider2::from(warehouse);
245 let hello_data = get_payload_v1(&provider.as_any_provider().as_downcasting()).unwrap();
246 assert!(matches!(
247 hello_data.get(),
248 HelloWorldV1 {
249 message: Cow::Borrowed(_),
250 }
251 ));
252 }
253
254 #[test]
255 fn test_provider2_dyn_erased_alt() {
256 let warehouse = get_warehouse(DATA);
257 let provider = DataProvider2::from(warehouse);
258 let hello_data = get_payload_alt(&provider.as_any_provider().as_downcasting()).unwrap();
259 assert!(matches!(hello_data.get(), HelloAlt { .. }));
260 }
261
262 #[test]
263 fn test_provider2_dyn_generic() {
264 let warehouse = get_warehouse(DATA);
265 let provider = DataProvider2::from(warehouse);
266 let hello_data =
267 get_payload_v1(&provider as &dyn DataProvider<HelloWorldV1Marker>).unwrap();
268 assert!(matches!(
269 hello_data.get(),
270 HelloWorldV1 {
271 message: Cow::Borrowed(_),
272 }
273 ));
274 }
275
276 #[test]
277 fn test_provider2_dyn_generic_alt() {
278 let warehouse = get_warehouse(DATA);
279 let provider = DataProvider2::from(warehouse);
280 let hello_data = get_payload_alt(&provider as &dyn DataProvider<HelloAltMarker>).unwrap();
281 assert!(matches!(hello_data.get(), HelloAlt { .. }));
282 }
283
284 #[test]
285 fn test_mismatched_types() {
286 let warehouse = get_warehouse(DATA);
287 let provider = DataProvider2::from(warehouse);
288 // Request is for v2, but type argument is for v1
289 let response: Result<DataResponse<HelloWorldV1Marker>, DataError> = AnyProvider::load_any(
290 &provider.as_any_provider(),
291 HELLO_ALT_KEY,
292 Default::default(),
293 )
294 .unwrap()
295 .downcast();
296 assert!(matches!(
297 response,
298 Err(DataError {
299 kind: DataErrorKind::MismatchedType(_),
300 ..
301 })
302 ));
303 }
304
305 fn check_v1_v2<P>(d: &P)
306 where
307 P: DataProvider<HelloWorldV1Marker> + DataProvider<HelloAltMarker> + ?Sized,
308 {
309 let v1: DataPayload<HelloWorldV1Marker> =
310 d.load(Default::default()).unwrap().take_payload().unwrap();
311 let v2: DataPayload<HelloAltMarker> =
312 d.load(Default::default()).unwrap().take_payload().unwrap();
313 if v1.get().message == v2.get().message {
314 panic!()
315 }
316 }
317
318 #[test]
319 fn test_v1_v2_generic() {
320 let warehouse = get_warehouse(DATA);
321 let provider = DataProvider2::from(warehouse);
322 check_v1_v2(&provider);
323 }
324
325 #[test]
326 fn test_v1_v2_dyn_erased() {
327 let warehouse = get_warehouse(DATA);
328 let provider = DataProvider2::from(warehouse);
329 check_v1_v2(&provider.as_any_provider().as_downcasting());
330 }
331}
332