1use crate::prelude::*;
8use crate::response::DataPayloadInner;
9use core::any::Any;
10use core::convert::TryFrom;
11use core::convert::TryInto;
12use yoke::trait_hack::YokeTraitHack;
13use yoke::Yokeable;
14use zerofrom::ZeroFrom;
15
16#[cfg(not(feature = "sync"))]
17use alloc::rc::Rc as SelectedRc;
18#[cfg(feature = "sync")]
19use alloc::sync::Arc as SelectedRc;
20
21#[cfg(feature = "sync")]
24pub trait MaybeSendSync: Send + Sync {}
25#[cfg(feature = "sync")]
26impl<T: Send + Sync> MaybeSendSync for T {}
27
28#[allow(missing_docs)] #[cfg(not(feature = "sync"))]
30pub trait MaybeSendSync {}
31#[cfg(not(feature = "sync"))]
32impl<T> MaybeSendSync for T {}
33
34#[derive(Debug, Clone)]
39enum AnyPayloadInner {
40 StructRef(&'static dyn Any),
42 #[cfg(not(feature = "sync"))]
49 PayloadRc(SelectedRc<dyn Any>),
50
51 #[cfg(feature = "sync")]
52 PayloadRc(SelectedRc<dyn Any + Send + Sync>),
53}
54
55#[derive(Debug, Clone, Yokeable)]
62pub struct AnyPayload {
63 inner: AnyPayloadInner,
64 type_name: &'static str,
65}
66
67#[allow(clippy::exhaustive_structs)] #[derive(Debug)]
70pub struct AnyMarker;
71
72impl DataMarker for AnyMarker {
73 type Yokeable = AnyPayload;
74}
75
76impl<M> crate::dynutil::UpcastDataPayload<M> for AnyMarker
77where
78 M: DataMarker,
79 M::Yokeable: MaybeSendSync,
80{
81 #[inline]
82 fn upcast(other: DataPayload<M>) -> DataPayload<AnyMarker> {
83 DataPayload::from_owned(other.wrap_into_any_payload())
84 }
85}
86
87impl AnyPayload {
88 pub fn downcast<M>(self) -> Result<DataPayload<M>, DataError>
94 where
95 M: DataMarker,
96 M::Yokeable: ZeroFrom<'static, M::Yokeable>,
98 M::Yokeable: MaybeSendSync,
100 for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Clone,
101 {
102 use AnyPayloadInner::*;
103 let type_name = self.type_name;
104 match self.inner {
105 StructRef(any_ref) => {
106 let down_ref: &'static M::Yokeable = any_ref
107 .downcast_ref()
108 .ok_or_else(|| DataError::for_type::<M>().with_str_context(type_name))?;
109 Ok(DataPayload::from_static_ref(down_ref))
110 }
111 PayloadRc(any_rc) => {
112 let down_rc = any_rc
113 .downcast::<DataPayload<M>>()
114 .map_err(|_| DataError::for_type::<M>().with_str_context(type_name))?;
115 Ok(SelectedRc::try_unwrap(down_rc).unwrap_or_else(|down_rc| (*down_rc).clone()))
116 }
117 }
118 }
119
120 pub fn downcast_cloned<M>(&self) -> Result<DataPayload<M>, DataError>
122 where
123 M: DataMarker,
124 M::Yokeable: ZeroFrom<'static, M::Yokeable>,
126 M::Yokeable: MaybeSendSync,
128 for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Clone,
129 {
130 self.clone().downcast()
131 }
132
133 pub fn from_static_ref<Y>(static_ref: &'static Y) -> Self
153 where
154 Y: for<'a> Yokeable<'a>,
155 {
156 AnyPayload {
157 inner: AnyPayloadInner::StructRef(static_ref),
158 type_name: core::any::type_name::<Y>(),
161 }
162 }
163}
164
165impl<M> DataPayload<M>
166where
167 M: DataMarker,
168 M::Yokeable: MaybeSendSync,
169{
170 pub fn wrap_into_any_payload(self) -> AnyPayload {
192 AnyPayload {
193 inner: match self.0 {
194 DataPayloadInner::StaticRef(r) => AnyPayloadInner::StructRef(r),
195 inner => AnyPayloadInner::PayloadRc(SelectedRc::from(Self(inner))),
196 },
197 type_name: core::any::type_name::<M>(),
198 }
199 }
200}
201
202impl DataPayload<AnyMarker> {
203 #[inline]
205 pub fn downcast<M>(self) -> Result<DataPayload<M>, DataError>
206 where
207 M: DataMarker,
208 for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Clone,
209 M::Yokeable: ZeroFrom<'static, M::Yokeable>,
210 M::Yokeable: MaybeSendSync,
211 {
212 self.try_unwrap_owned()?.downcast()
213 }
214}
215
216#[allow(clippy::exhaustive_structs)] #[derive(Debug)]
221pub struct AnyResponse {
222 pub metadata: DataResponseMetadata,
224
225 pub payload: Option<AnyPayload>,
227}
228
229impl TryFrom<DataResponse<AnyMarker>> for AnyResponse {
230 type Error = DataError;
231 #[inline]
232 fn try_from(other: DataResponse<AnyMarker>) -> Result<Self, Self::Error> {
233 Ok(Self {
234 metadata: other.metadata,
235 payload: other.payload.map(|p| p.try_unwrap_owned()).transpose()?,
236 })
237 }
238}
239
240impl From<AnyResponse> for DataResponse<AnyMarker> {
241 #[inline]
242 fn from(other: AnyResponse) -> Self {
243 Self {
244 metadata: other.metadata,
245 payload: other.payload.map(DataPayload::from_owned),
246 }
247 }
248}
249
250impl AnyResponse {
251 #[inline]
253 pub fn downcast<M>(self) -> Result<DataResponse<M>, DataError>
254 where
255 M: DataMarker,
256 for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Clone,
257 M::Yokeable: ZeroFrom<'static, M::Yokeable>,
258 M::Yokeable: MaybeSendSync,
259 {
260 Ok(DataResponse {
261 metadata: self.metadata,
262 payload: self.payload.map(|p| p.downcast()).transpose()?,
263 })
264 }
265
266 pub fn downcast_cloned<M>(&self) -> Result<DataResponse<M>, DataError>
268 where
269 M: DataMarker,
270 M::Yokeable: ZeroFrom<'static, M::Yokeable>,
271 M::Yokeable: MaybeSendSync,
272 for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Clone,
273 {
274 Ok(DataResponse {
275 metadata: self.metadata.clone(),
276 payload: self
277 .payload
278 .as_ref()
279 .map(|p| p.downcast_cloned())
280 .transpose()?,
281 })
282 }
283}
284
285impl<M> DataResponse<M>
286where
287 M: DataMarker,
288 M::Yokeable: MaybeSendSync,
289{
290 pub fn wrap_into_any_response(self) -> AnyResponse {
293 AnyResponse {
294 metadata: self.metadata,
295 payload: self.payload.map(|p| p.wrap_into_any_payload()),
296 }
297 }
298}
299
300pub trait AnyProvider {
348 fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError>;
350}
351
352impl<'a, T: AnyProvider + ?Sized> AnyProvider for &'a T {
353 #[inline]
354 fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
355 (**self).load_any(key, req)
356 }
357}
358
359impl<T: AnyProvider + ?Sized> AnyProvider for alloc::boxed::Box<T> {
360 #[inline]
361 fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
362 (**self).load_any(key, req)
363 }
364}
365
366impl<T: AnyProvider + ?Sized> AnyProvider for alloc::rc::Rc<T> {
367 #[inline]
368 fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
369 (**self).load_any(key, req)
370 }
371}
372
373#[cfg(target_has_atomic = "ptr")]
374impl<T: AnyProvider + ?Sized> AnyProvider for alloc::sync::Arc<T> {
375 #[inline]
376 fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
377 (**self).load_any(key, req)
378 }
379}
380
381#[allow(clippy::exhaustive_structs)] #[derive(Debug)]
384pub struct DynamicDataProviderAnyMarkerWrap<'a, P: ?Sized>(pub &'a P);
385
386pub trait AsDynamicDataProviderAnyMarkerWrap {
388 fn as_any_provider(&self) -> DynamicDataProviderAnyMarkerWrap<Self>;
390}
391
392impl<P> AsDynamicDataProviderAnyMarkerWrap for P
393where
394 P: DynamicDataProvider<AnyMarker> + ?Sized,
395{
396 #[inline]
397 fn as_any_provider(&self) -> DynamicDataProviderAnyMarkerWrap<P> {
398 DynamicDataProviderAnyMarkerWrap(self)
399 }
400}
401
402impl<P> AnyProvider for DynamicDataProviderAnyMarkerWrap<'_, P>
403where
404 P: DynamicDataProvider<AnyMarker> + ?Sized,
405{
406 #[inline]
407 fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
408 self.0.load_data(key, req)?.try_into()
409 }
410}
411
412#[allow(clippy::exhaustive_structs)] #[derive(Debug)]
415pub struct DowncastingAnyProvider<'a, P: ?Sized>(pub &'a P);
416
417pub trait AsDowncastingAnyProvider {
419 fn as_downcasting(&self) -> DowncastingAnyProvider<Self>;
421}
422
423impl<P> AsDowncastingAnyProvider for P
424where
425 P: AnyProvider + ?Sized,
426{
427 #[inline]
428 fn as_downcasting(&self) -> DowncastingAnyProvider<P> {
429 DowncastingAnyProvider(self)
430 }
431}
432
433impl<M, P> DataProvider<M> for DowncastingAnyProvider<'_, P>
434where
435 P: AnyProvider + ?Sized,
436 M: KeyedDataMarker,
437 for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Clone,
438 M::Yokeable: ZeroFrom<'static, M::Yokeable>,
439 M::Yokeable: MaybeSendSync,
440{
441 #[inline]
442 fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
443 self.0
444 .load_any(M::KEY, req)?
445 .downcast()
446 .map_err(|e| e.with_req(M::KEY, req))
447 }
448}
449
450impl<M, P> DynamicDataProvider<M> for DowncastingAnyProvider<'_, P>
451where
452 P: AnyProvider + ?Sized,
453 M: DataMarker,
454 for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Clone,
455 M::Yokeable: ZeroFrom<'static, M::Yokeable>,
456 M::Yokeable: MaybeSendSync,
457{
458 #[inline]
459 fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError> {
460 self.0
461 .load_any(key, req)?
462 .downcast()
463 .map_err(|e| e.with_req(key, req))
464 }
465}
466
467#[cfg(test)]
468mod test {
469 use super::*;
470 use crate::hello_world::*;
471 use alloc::borrow::Cow;
472
473 const CONST_DATA: HelloWorldV1<'static> = HelloWorldV1 {
474 message: Cow::Borrowed("Custom Hello World"),
475 };
476
477 #[test]
478 fn test_debug() {
479 let payload: DataPayload<HelloWorldV1Marker> = DataPayload::from_owned(HelloWorldV1 {
480 message: Cow::Borrowed("Custom Hello World"),
481 });
482
483 let any_payload = payload.wrap_into_any_payload();
484 assert_eq!(
485 "AnyPayload { inner: PayloadRc(Any { .. }), type_name: \"icu_provider::hello_world::HelloWorldV1Marker\" }",
486 format!("{any_payload:?}")
487 );
488
489 struct WrongMarker;
490
491 impl DataMarker for WrongMarker {
492 type Yokeable = u8;
493 }
494
495 let err = any_payload.downcast::<WrongMarker>().unwrap_err();
496 assert_eq!(
497 "ICU4X data error: Mismatched types: tried to downcast with icu_provider::any::test::test_debug::WrongMarker, but actual type is different: icu_provider::hello_world::HelloWorldV1Marker",
498 format!("{err}")
499 );
500 }
501
502 #[test]
503 fn test_non_owned_any_marker() {
504 let payload_result: DataPayload<AnyMarker> =
506 DataPayload::from_owned_buffer(Box::new(*b"pretend we're borrowing from here"))
507 .map_project(|_, _| AnyPayload::from_static_ref(&CONST_DATA));
508 let err = payload_result.downcast::<HelloWorldV1Marker>().unwrap_err();
509 assert!(matches!(
510 err,
511 DataError {
512 kind: DataErrorKind::InvalidState,
513 ..
514 }
515 ));
516 }
517}