From 4e8199b572f2035b7749cba276ece3a26630d23e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:18:21 +0200 Subject: Adding upstream version 1.67.1+dfsg1. Signed-off-by: Daniel Baumann --- vendor/icu_provider/src/any.rs | 416 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 vendor/icu_provider/src/any.rs (limited to 'vendor/icu_provider/src/any.rs') diff --git a/vendor/icu_provider/src/any.rs b/vendor/icu_provider/src/any.rs new file mode 100644 index 000000000..1c7a60435 --- /dev/null +++ b/vendor/icu_provider/src/any.rs @@ -0,0 +1,416 @@ +// 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 ). + +//! Traits for data providers that produce `Any` objects. + +use crate::prelude::*; +use core::any::Any; +use core::convert::TryFrom; +use core::convert::TryInto; +use yoke::trait_hack::YokeTraitHack; +use yoke::Yokeable; +use zerofrom::ZeroFrom; + +#[cfg(not(feature = "sync"))] +use alloc::rc::Rc as SelectedRc; +#[cfg(feature = "sync")] +use alloc::sync::Arc as SelectedRc; + +/// A trait that allows to specify `Send + Sync` bounds that are only required when +/// the `sync` feature is enabled. Without the feature, this is an empty bound. +#[cfg(feature = "sync")] +pub trait MaybeSendSync: Send + Sync {} +#[cfg(feature = "sync")] +impl MaybeSendSync for T {} + +#[allow(missing_docs)] // docs generated with all features +#[cfg(not(feature = "sync"))] +pub trait MaybeSendSync {} +#[cfg(not(feature = "sync"))] +impl MaybeSendSync for T {} + +/// Representations of the `Any` trait object. +/// +/// **Important Note:** The types enclosed by `StructRef` and `PayloadRc` are NOT the same! +/// The first refers to the struct itself, whereas the second refers to a `DataPayload`. +#[derive(Debug, Clone)] +enum AnyPayloadInner { + /// A reference to `M::Yokeable` + StructRef(&'static dyn Any), + /// A boxed `DataPayload`. + /// + /// Note: This needs to be reference counted, not a `Box`, so that `AnyPayload` is cloneable. + /// If an `AnyPayload` is cloned, the actual cloning of the data is delayed until + /// `downcast()` is invoked (at which point we have the concrete type). + + #[cfg(not(feature = "sync"))] + PayloadRc(SelectedRc), + + #[cfg(feature = "sync")] + PayloadRc(SelectedRc), +} + +/// A type-erased data payload. +/// +/// The only useful method on this type is [`AnyPayload::downcast()`], which transforms this into +/// a normal `DataPayload` which you can subsequently access or mutate. +/// +/// As with `DataPayload`, cloning is designed to be cheap. +#[derive(Debug, Clone, Yokeable)] +pub struct AnyPayload { + inner: AnyPayloadInner, + type_name: &'static str, +} + +/// The [`DataMarker`] marker type for [`AnyPayload`]. +#[allow(clippy::exhaustive_structs)] // marker type +pub struct AnyMarker; + +impl DataMarker for AnyMarker { + type Yokeable = AnyPayload; +} + +impl crate::dynutil::UpcastDataPayload for AnyMarker +where + M: DataMarker + 'static, + M::Yokeable: MaybeSendSync, +{ + #[inline] + fn upcast(other: DataPayload) -> DataPayload { + DataPayload::from_owned(other.wrap_into_any_payload()) + } +} + +impl AnyPayload { + /// Transforms a type-erased `AnyPayload` into a concrete `DataPayload`. + /// + /// Because it is expected that the call site knows the identity of the AnyPayload (e.g., from + /// the data request), this function returns a `DataError` if the generic type does not match + /// the type stored in the `AnyPayload`. + pub fn downcast(self) -> Result, DataError> + where + M: DataMarker + 'static, + // For the StructRef case: + M::Yokeable: ZeroFrom<'static, M::Yokeable>, + // For the PayloadRc case: + M::Yokeable: MaybeSendSync, + for<'a> YokeTraitHack<>::Output>: Clone, + { + use AnyPayloadInner::*; + let type_name = self.type_name; + match self.inner { + StructRef(any_ref) => { + let down_ref: &'static M::Yokeable = any_ref + .downcast_ref() + .ok_or_else(|| DataError::for_type::().with_str_context(type_name))?; + Ok(DataPayload::from_owned(M::Yokeable::zero_from(down_ref))) + } + PayloadRc(any_rc) => { + let down_rc = any_rc + .downcast::>() + .map_err(|_| DataError::for_type::().with_str_context(type_name))?; + Ok(SelectedRc::try_unwrap(down_rc).unwrap_or_else(|down_rc| (*down_rc).clone())) + } + } + } + + /// Creates an `AnyPayload` from a static reference to a data struct. + /// + /// # Examples + /// + /// ``` + /// use icu_provider::hello_world::*; + /// use icu_provider::prelude::*; + /// use std::borrow::Cow; + /// + /// const HELLO_DATA: HelloWorldV1<'static> = HelloWorldV1 { + /// message: Cow::Borrowed("Custom Hello World"), + /// }; + /// + /// let any_payload = AnyPayload::from_static_ref(&HELLO_DATA); + /// + /// let payload: DataPayload = + /// any_payload.downcast().expect("TypeId matches"); + /// assert_eq!("Custom Hello World", payload.get().message); + /// ``` + pub fn from_static_ref(static_ref: &'static Y) -> Self + where + Y: for<'a> Yokeable<'a>, + { + AnyPayload { + inner: AnyPayloadInner::StructRef(static_ref), + // Note: This records the Yokeable type rather than the DataMarker type, + // but that is okay since this is only for debugging + type_name: core::any::type_name::(), + } + } +} + +impl DataPayload +where + M: DataMarker + 'static, + M::Yokeable: MaybeSendSync, +{ + /// Moves this DataPayload to the heap (requiring an allocation) and returns it as an + /// erased `AnyPayload`. + /// + /// # Examples + /// + /// ``` + /// use icu_provider::hello_world::*; + /// use icu_provider::prelude::*; + /// use std::borrow::Cow; + /// use std::rc::Rc; + /// + /// let payload: DataPayload = + /// DataPayload::from_owned(HelloWorldV1 { + /// message: Cow::Borrowed("Custom Hello World"), + /// }); + /// + /// let any_payload = payload.wrap_into_any_payload(); + /// + /// let payload: DataPayload = + /// any_payload.downcast().expect("TypeId matches"); + /// assert_eq!("Custom Hello World", payload.get().message); + /// ``` + pub fn wrap_into_any_payload(self) -> AnyPayload { + AnyPayload { + inner: AnyPayloadInner::PayloadRc(SelectedRc::from(self)), + type_name: core::any::type_name::(), + } + } +} + +impl DataPayload { + /// Transforms a type-erased `DataPayload` into a concrete `DataPayload`. + #[inline] + pub fn downcast(self) -> Result, DataError> + where + M: DataMarker + 'static, + for<'a> YokeTraitHack<>::Output>: Clone, + M::Yokeable: ZeroFrom<'static, M::Yokeable>, + M::Yokeable: MaybeSendSync, + { + self.try_unwrap_owned()?.downcast() + } +} + +/// A [`DataResponse`] for type-erased values. +/// +/// Convertible to and from `DataResponse`. +#[allow(clippy::exhaustive_structs)] // this type is stable (the metadata is allowed to grow) +pub struct AnyResponse { + /// Metadata about the returned object. + pub metadata: DataResponseMetadata, + + /// The object itself; None if it was not loaded. + pub payload: Option, +} + +impl TryFrom> for AnyResponse { + type Error = DataError; + #[inline] + fn try_from(other: DataResponse) -> Result { + Ok(Self { + metadata: other.metadata, + payload: other.payload.map(|p| p.try_unwrap_owned()).transpose()?, + }) + } +} + +impl From for DataResponse { + #[inline] + fn from(other: AnyResponse) -> Self { + Self { + metadata: other.metadata, + payload: other.payload.map(DataPayload::from_owned), + } + } +} + +impl AnyResponse { + /// Transforms a type-erased `DataResponse` into a concrete `DataResponse`. + #[inline] + pub fn downcast(self) -> Result, DataError> + where + M: DataMarker + 'static, + for<'a> YokeTraitHack<>::Output>: Clone, + M::Yokeable: ZeroFrom<'static, M::Yokeable>, + M::Yokeable: MaybeSendSync, + { + Ok(DataResponse { + metadata: self.metadata, + payload: self.payload.map(|p| p.downcast()).transpose()?, + }) + } +} + +/// An object-safe data provider that returns data structs cast to `dyn Any` trait objects. +/// +/// # Examples +/// +/// ``` +/// use icu_provider::hello_world::*; +/// use icu_provider::prelude::*; +/// use std::borrow::Cow; +/// +/// let any_response = HelloWorldProvider +/// .as_any_provider() +/// .load_any( +/// HelloWorldV1Marker::KEY, +/// DataRequest { +/// locale: &icu_locid::locale!("de").into(), +/// metadata: Default::default(), +/// }, +/// ) +/// .expect("Load should succeed"); +/// +/// // Downcast to something useful +/// let response: DataResponse = +/// any_response.downcast().expect("Types match"); +/// +/// let payload = response.take_payload().expect("Data should be present"); +/// +/// assert_eq!(payload.get().message, "Hallo Welt"); +/// ``` +pub trait AnyProvider { + /// Loads an [`AnyPayload`] according to the key and request. + fn load_any(&self, key: DataKey, req: DataRequest) -> Result; +} + +impl AnyProvider for alloc::boxed::Box { + fn load_any(&self, key: DataKey, req: DataRequest) -> Result { + (**self).load_any(key, req) + } +} + +/// A wrapper over `DynamicDataProvider` that implements `AnyProvider` +#[allow(clippy::exhaustive_structs)] // newtype +pub struct DynamicDataProviderAnyMarkerWrap<'a, P: ?Sized>(pub &'a P); + +/// Blanket-implemented trait adding the [`Self::as_any_provider()`] function. +pub trait AsDynamicDataProviderAnyMarkerWrap { + /// Returns an object implementing `AnyProvider` when called on `DynamicDataProvider` + fn as_any_provider(&self) -> DynamicDataProviderAnyMarkerWrap; +} + +impl

AsDynamicDataProviderAnyMarkerWrap for P +where + P: DynamicDataProvider, +{ + #[inline] + fn as_any_provider(&self) -> DynamicDataProviderAnyMarkerWrap

{ + DynamicDataProviderAnyMarkerWrap(self) + } +} + +impl

AnyProvider for DynamicDataProviderAnyMarkerWrap<'_, P> +where + P: DynamicDataProvider + ?Sized, +{ + #[inline] + fn load_any(&self, key: DataKey, req: DataRequest) -> Result { + self.0.load_data(key, req)?.try_into() + } +} + +/// A wrapper over `AnyProvider` that implements `DynamicDataProvider` via downcasting +#[allow(clippy::exhaustive_structs)] // newtype +pub struct DowncastingAnyProvider<'a, P: ?Sized>(pub &'a P); + +/// Blanket-implemented trait adding the [`Self::as_downcasting()`] function. +pub trait AsDowncastingAnyProvider { + /// Returns an object implementing `DynamicDataProvider` when called on `AnyProvider` + fn as_downcasting(&self) -> DowncastingAnyProvider; +} + +impl

AsDowncastingAnyProvider for P +where + P: AnyProvider + ?Sized, +{ + #[inline] + fn as_downcasting(&self) -> DowncastingAnyProvider

{ + DowncastingAnyProvider(self) + } +} + +impl DataProvider for DowncastingAnyProvider<'_, P> +where + P: AnyProvider + ?Sized, + M: KeyedDataMarker + 'static, + for<'a> YokeTraitHack<>::Output>: Clone, + M::Yokeable: ZeroFrom<'static, M::Yokeable>, + M::Yokeable: MaybeSendSync, +{ + #[inline] + fn load(&self, req: DataRequest) -> Result, DataError> { + self.0.load_any(M::KEY, req)?.downcast() + } +} + +impl DynamicDataProvider for DowncastingAnyProvider<'_, P> +where + P: AnyProvider + ?Sized, + M: DataMarker + 'static, + for<'a> YokeTraitHack<>::Output>: Clone, + M::Yokeable: ZeroFrom<'static, M::Yokeable>, + M::Yokeable: MaybeSendSync, +{ + #[inline] + fn load_data(&self, key: DataKey, req: DataRequest) -> Result, DataError> { + self.0.load_any(key, req)?.downcast() + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::hello_world::*; + use alloc::borrow::Cow; + + const CONST_DATA: HelloWorldV1<'static> = HelloWorldV1 { + message: Cow::Borrowed("Custom Hello World"), + }; + + #[test] + fn test_debug() { + let payload: DataPayload = DataPayload::from_owned(HelloWorldV1 { + message: Cow::Borrowed("Custom Hello World"), + }); + + let any_payload = payload.wrap_into_any_payload(); + assert_eq!( + "AnyPayload { inner: PayloadRc(Any { .. }), type_name: \"icu_provider::hello_world::HelloWorldV1Marker\" }", + format!("{:?}", any_payload) + ); + + struct WrongMarker; + + impl DataMarker for WrongMarker { + type Yokeable = u8; + } + + let err = any_payload.downcast::().unwrap_err(); + assert_eq!( + "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", + format!("{}", err) + ); + } + + #[test] + fn test_non_owned_any_marker() { + // This test demonstrates a code path that can trigger the InvalidState error kind. + let payload_result: DataPayload = + DataPayload::from_owned_buffer(Box::new(*b"pretend we're borrowing from here")) + .map_project(|_, _| AnyPayload::from_static_ref(&CONST_DATA)); + let err = payload_result.downcast::().unwrap_err(); + assert!(matches!( + err, + DataError { + kind: DataErrorKind::InvalidState, + .. + } + )); + } +} -- cgit v1.2.3