summaryrefslogtreecommitdiffstats
path: root/third_party/rust/icu_provider/src/response.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/icu_provider/src/response.rs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/icu_provider/src/response.rs')
-rw-r--r--third_party/rust/icu_provider/src/response.rs748
1 files changed, 748 insertions, 0 deletions
diff --git a/third_party/rust/icu_provider/src/response.rs b/third_party/rust/icu_provider/src/response.rs
new file mode 100644
index 0000000000..09b11830fa
--- /dev/null
+++ b/third_party/rust/icu_provider/src/response.rs
@@ -0,0 +1,748 @@
+// 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::buf::BufferMarker;
+use crate::error::{DataError, DataErrorKind};
+use crate::marker::DataMarker;
+use crate::request::DataLocale;
+use alloc::boxed::Box;
+use core::convert::TryFrom;
+use core::fmt::Debug;
+use core::marker::PhantomData;
+use core::ops::Deref;
+use yoke::trait_hack::YokeTraitHack;
+use yoke::*;
+
+#[cfg(not(feature = "sync"))]
+use alloc::rc::Rc as SelectedRc;
+#[cfg(feature = "sync")]
+use alloc::sync::Arc as SelectedRc;
+
+/// A response object containing metadata about the returned data.
+#[derive(Debug, Clone, PartialEq, Default)]
+#[non_exhaustive]
+pub struct DataResponseMetadata {
+ /// The resolved locale of the returned data, if locale fallbacking was performed.
+ pub locale: Option<DataLocale>,
+ /// The format of the buffer for buffer-backed data, if known (for example, JSON).
+ pub buffer_format: Option<crate::buf::BufferFormat>,
+}
+
+/// A container for data payloads returned from a data provider.
+///
+/// [`DataPayload`] is built on top of the [`yoke`] framework, which allows for cheap, zero-copy
+/// operations on data via the use of self-references.
+///
+/// The type of the data stored in [`DataPayload`] is determined by the [`DataMarker`] type parameter.
+///
+/// ## Accessing the data
+///
+/// To get a reference to the data inside [`DataPayload`], use [`DataPayload::get()`]. If you need
+/// to store the data for later use, you need to store the [`DataPayload`] itself, since `get` only
+/// returns a reference with an ephemeral lifetime.
+///
+/// ## Mutating the data
+///
+/// To modify the data stored in a [`DataPayload`], use [`DataPayload::with_mut()`].
+///
+/// ## Transforming the data to a different type
+///
+/// To transform a [`DataPayload`] to a different type backed by the same data store (cart), use
+/// [`DataPayload::map_project()`] or one of its sister methods.
+///
+/// # Cargo feature: `sync`
+///
+/// By default, the payload uses non-concurrent reference counting internally, and hence is neither
+/// [`Sync`] nor [`Send`]; if these traits are required, the `sync` Cargo feature can be enabled.
+///
+/// # Examples
+///
+/// Basic usage, using the `HelloWorldV1Marker` marker:
+///
+/// ```
+/// use icu_provider::hello_world::*;
+/// use icu_provider::prelude::*;
+/// use std::borrow::Cow;
+///
+/// let payload = DataPayload::<HelloWorldV1Marker>::from_owned(HelloWorldV1 {
+/// message: Cow::Borrowed("Demo"),
+/// });
+///
+/// assert_eq!("Demo", payload.get().message);
+/// ```
+pub struct DataPayload<M: DataMarker>(pub(crate) DataPayloadInner<M>);
+
+pub(crate) enum DataPayloadInner<M: DataMarker> {
+ Yoke(Yoke<M::Yokeable, Option<Cart>>),
+ StaticRef(&'static M::Yokeable),
+}
+
+/// The type of the "cart" that is used by `DataPayload`.
+#[derive(Clone, Debug)]
+#[allow(clippy::redundant_allocation)] // false positive, it's cheaper to wrap an existing Box in an Rc than to reallocate a huge Rc
+pub struct Cart(SelectedRc<Box<[u8]>>);
+
+impl Deref for Cart {
+ type Target = Box<[u8]>;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+// Safe because both Rc and Arc are StableDeref, and our impl delegates.
+unsafe impl stable_deref_trait::StableDeref for Cart {}
+// Safe because both Rc and Arc are CloneableCart, and our impl delegates.
+unsafe impl yoke::CloneableCart for Cart {}
+
+impl Cart {
+ /// Creates a `Yoke<Y, Option<Cart>>` from owned bytes by applying `f`.
+ pub fn try_make_yoke<Y, F, E>(cart: Box<[u8]>, f: F) -> Result<Yoke<Y, Option<Self>>, E>
+ where
+ for<'a> Y: Yokeable<'a>,
+ F: FnOnce(&[u8]) -> Result<<Y as Yokeable>::Output, E>,
+ {
+ Yoke::try_attach_to_cart(SelectedRc::new(cart), |b| f(b))
+ // Safe because the cart is only wrapped
+ .map(|yoke| unsafe { yoke.replace_cart(Cart) })
+ .map(Yoke::wrap_cart_in_option)
+ }
+}
+
+impl<M> Debug for DataPayload<M>
+where
+ M: DataMarker,
+ for<'a> &'a <M::Yokeable as Yokeable<'a>>::Output: Debug,
+{
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ self.get().fmt(f)
+ }
+}
+
+/// Cloning a DataPayload is generally a cheap operation.
+/// See notes in the `Clone` impl for [`Yoke`].
+///
+/// # Examples
+///
+/// ```no_run
+/// use icu_provider::hello_world::*;
+/// use icu_provider::prelude::*;
+///
+/// let resp1: DataPayload<HelloWorldV1Marker> = todo!();
+/// let resp2 = resp1.clone();
+/// ```
+impl<M> Clone for DataPayload<M>
+where
+ M: DataMarker,
+ for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Clone,
+{
+ fn clone(&self) -> Self {
+ Self(match &self.0 {
+ DataPayloadInner::Yoke(yoke) => DataPayloadInner::Yoke(yoke.clone()),
+ DataPayloadInner::StaticRef(r) => DataPayloadInner::StaticRef(*r),
+ })
+ }
+}
+
+impl<M> PartialEq for DataPayload<M>
+where
+ M: DataMarker,
+ for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: PartialEq,
+{
+ fn eq(&self, other: &Self) -> bool {
+ YokeTraitHack(self.get()).into_ref() == YokeTraitHack(other.get()).into_ref()
+ }
+}
+
+impl<M> Eq for DataPayload<M>
+where
+ M: DataMarker,
+ for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Eq,
+{
+}
+
+#[test]
+fn test_clone_eq() {
+ use crate::hello_world::*;
+ let p1 = DataPayload::<HelloWorldV1Marker>::from_static_str("Demo");
+ #[allow(clippy::redundant_clone)]
+ let p2 = p1.clone();
+ assert_eq!(p1, p2);
+}
+
+impl<M> DataPayload<M>
+where
+ M: DataMarker,
+{
+ /// Convert a fully owned (`'static`) data struct into a DataPayload.
+ ///
+ /// This constructor creates `'static` payloads.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use icu_provider::hello_world::*;
+ /// use icu_provider::prelude::*;
+ /// use std::borrow::Cow;
+ ///
+ /// let local_struct = HelloWorldV1 {
+ /// message: Cow::Owned("example".to_owned()),
+ /// };
+ ///
+ /// let payload =
+ /// DataPayload::<HelloWorldV1Marker>::from_owned(local_struct.clone());
+ ///
+ /// assert_eq!(payload.get(), &local_struct);
+ /// ```
+ #[inline]
+ pub const fn from_owned(data: M::Yokeable) -> Self {
+ Self(DataPayloadInner::Yoke(Yoke::new_owned(data)))
+ }
+
+ #[doc(hidden)]
+ #[inline]
+ pub const fn from_static_ref(data: &'static M::Yokeable) -> Self {
+ Self(DataPayloadInner::StaticRef(data))
+ }
+
+ /// Convert a DataPayload that was created via [`DataPayload::from_owned()`] back into the
+ /// concrete type used to construct it.
+ pub fn try_unwrap_owned(self) -> Result<M::Yokeable, DataError> {
+ match self.0 {
+ DataPayloadInner::Yoke(yoke) => yoke.try_into_yokeable().ok(),
+ DataPayloadInner::StaticRef(_) => None,
+ }
+ .ok_or(DataErrorKind::InvalidState.with_str_context("try_unwrap_owned"))
+ }
+
+ /// Mutate the data contained in this DataPayload.
+ ///
+ /// For safety, all mutation operations must take place within a helper function that cannot
+ /// borrow data from the surrounding context.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use icu_provider::hello_world::HelloWorldV1Marker;
+ /// use icu_provider::prelude::*;
+ ///
+ /// let mut payload =
+ /// DataPayload::<HelloWorldV1Marker>::from_static_str("Hello");
+ ///
+ /// payload.with_mut(|s| s.message.to_mut().push_str(" World"));
+ ///
+ /// assert_eq!("Hello World", payload.get().message);
+ /// ```
+ ///
+ /// To transfer data from the context into the data struct, use the `move` keyword:
+ ///
+ /// ```
+ /// use icu_provider::hello_world::HelloWorldV1Marker;
+ /// use icu_provider::prelude::*;
+ ///
+ /// let mut payload =
+ /// DataPayload::<HelloWorldV1Marker>::from_static_str("Hello");
+ ///
+ /// let suffix = " World";
+ /// payload.with_mut(move |s| s.message.to_mut().push_str(suffix));
+ ///
+ /// assert_eq!("Hello World", payload.get().message);
+ /// ```
+ pub fn with_mut<'a, F>(&'a mut self, f: F)
+ where
+ F: 'static + for<'b> FnOnce(&'b mut <M::Yokeable as Yokeable<'a>>::Output),
+ M::Yokeable: zerofrom::ZeroFrom<'static, M::Yokeable>,
+ {
+ if let DataPayloadInner::StaticRef(r) = self.0 {
+ self.0 = DataPayloadInner::Yoke(Yoke::new_owned(zerofrom::ZeroFrom::zero_from(r)));
+ }
+ match &mut self.0 {
+ DataPayloadInner::Yoke(yoke) => yoke.with_mut(f),
+ _ => unreachable!(),
+ }
+ }
+
+ /// Borrows the underlying data.
+ ///
+ /// This function should be used like `Deref` would normally be used. For more information on
+ /// why DataPayload cannot implement `Deref`, see the `yoke` crate.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use icu_provider::hello_world::HelloWorldV1Marker;
+ /// use icu_provider::prelude::*;
+ ///
+ /// let payload = DataPayload::<HelloWorldV1Marker>::from_static_str("Demo");
+ ///
+ /// assert_eq!("Demo", payload.get().message);
+ /// ```
+ #[inline]
+ #[allow(clippy::needless_lifetimes)]
+ pub fn get<'a>(&'a self) -> &'a <M::Yokeable as Yokeable<'a>>::Output {
+ match &self.0 {
+ DataPayloadInner::Yoke(yoke) => yoke.get(),
+ DataPayloadInner::StaticRef(r) => Yokeable::transform(*r),
+ }
+ }
+
+ /// Maps `DataPayload<M>` to `DataPayload<M2>` by projecting it with [`Yoke::map_project`].
+ ///
+ /// This is accomplished by a function that takes `M`'s data type and returns `M2`'s data
+ /// type. The function takes a second argument which should be ignored. For more details,
+ /// see [`Yoke::map_project()`].
+ ///
+ /// The standard [`DataPayload::map_project()`] function moves `self` and cannot capture any
+ /// data from its context. Use one of the sister methods if you need these capabilities:
+ ///
+ /// - [`DataPayload::map_project_cloned()`] if you don't have ownership of `self`
+ /// - [`DataPayload::try_map_project()`] to bubble up an error
+ /// - [`DataPayload::try_map_project_cloned()`] to do both of the above
+ ///
+ /// # Examples
+ ///
+ /// Map from `HelloWorldV1` to a `Cow<str>` containing just the message:
+ ///
+ /// ```
+ /// use icu_provider::hello_world::*;
+ /// use icu_provider::prelude::*;
+ /// use std::borrow::Cow;
+ ///
+ /// // A custom marker type is required when using `map_project`. The Yokeable should be the
+ /// // target type, and the Cart should correspond to the type being transformed.
+ ///
+ /// struct HelloWorldV1MessageMarker;
+ /// impl DataMarker for HelloWorldV1MessageMarker {
+ /// type Yokeable = Cow<'static, str>;
+ /// }
+ ///
+ /// let p1: DataPayload<HelloWorldV1Marker> = DataPayload::from_owned(HelloWorldV1 {
+ /// message: Cow::Borrowed("Hello World"),
+ /// });
+ ///
+ /// assert_eq!("Hello World", p1.get().message);
+ ///
+ /// let p2: DataPayload<HelloWorldV1MessageMarker> = p1.map_project(|obj, _| obj.message);
+ ///
+ /// // Note: at this point, p1 has been moved.
+ /// assert_eq!("Hello World", p2.get());
+ /// ```
+ #[allow(clippy::type_complexity)]
+ pub fn map_project<M2, F>(self, f: F) -> DataPayload<M2>
+ where
+ M2: DataMarker,
+ F: for<'a> FnOnce(
+ <M::Yokeable as Yokeable<'a>>::Output,
+ PhantomData<&'a ()>,
+ ) -> <M2::Yokeable as Yokeable<'a>>::Output,
+ M::Yokeable: zerofrom::ZeroFrom<'static, M::Yokeable>,
+ {
+ DataPayload(DataPayloadInner::Yoke(
+ match self.0 {
+ DataPayloadInner::Yoke(yoke) => yoke,
+ DataPayloadInner::StaticRef(r) => Yoke::new_owned(zerofrom::ZeroFrom::zero_from(r)),
+ }
+ .map_project(f),
+ ))
+ }
+
+ /// Version of [`DataPayload::map_project()`] that borrows `self` instead of moving `self`.
+ ///
+ /// # Examples
+ ///
+ /// Same example as above, but this time, do not move out of `p1`:
+ ///
+ /// ```
+ /// // Same imports and definitions as above
+ /// # use icu_provider::hello_world::*;
+ /// # use icu_provider::prelude::*;
+ /// # use std::borrow::Cow;
+ /// # struct HelloWorldV1MessageMarker;
+ /// # impl DataMarker for HelloWorldV1MessageMarker {
+ /// # type Yokeable = Cow<'static, str>;
+ /// # }
+ ///
+ /// let p1: DataPayload<HelloWorldV1Marker> =
+ /// DataPayload::from_owned(HelloWorldV1 {
+ /// message: Cow::Borrowed("Hello World"),
+ /// });
+ ///
+ /// assert_eq!("Hello World", p1.get().message);
+ ///
+ /// let p2: DataPayload<HelloWorldV1MessageMarker> =
+ /// p1.map_project_cloned(|obj, _| obj.message.clone());
+ ///
+ /// // Note: p1 is still valid.
+ /// assert_eq!(p1.get().message, *p2.get());
+ /// ```
+ #[allow(clippy::type_complexity)]
+ pub fn map_project_cloned<'this, M2, F>(&'this self, f: F) -> DataPayload<M2>
+ where
+ M2: DataMarker,
+ F: for<'a> FnOnce(
+ &'this <M::Yokeable as Yokeable<'a>>::Output,
+ PhantomData<&'a ()>,
+ ) -> <M2::Yokeable as Yokeable<'a>>::Output,
+ {
+ DataPayload(DataPayloadInner::Yoke(match &self.0 {
+ DataPayloadInner::Yoke(yoke) => yoke.map_project_cloned(f),
+ DataPayloadInner::StaticRef(r) => {
+ let output: <M2::Yokeable as Yokeable<'static>>::Output =
+ f(Yokeable::transform(*r), PhantomData);
+ // Safety: <M2::Yokeable as Yokeable<'static>>::Output is the same type as M2::Yokeable;
+ // we're going from 'static to 'static, however in a generic context it's not
+ // clear to the compiler that that is the case. We have to use the unsafe make API to do this.
+ let yokeable: M2::Yokeable = unsafe { M2::Yokeable::make(output) };
+ Yoke::new_owned(yokeable)
+ }
+ }))
+ }
+
+ /// Version of [`DataPayload::map_project()`] that bubbles up an error from `f`.
+ ///
+ /// # Examples
+ ///
+ /// Same example as above, but bubble up an error:
+ ///
+ /// ```
+ /// // Same imports and definitions as above
+ /// # use icu_provider::hello_world::*;
+ /// # use icu_provider::prelude::*;
+ /// # use std::borrow::Cow;
+ /// # struct HelloWorldV1MessageMarker;
+ /// # impl DataMarker for HelloWorldV1MessageMarker {
+ /// # type Yokeable = Cow<'static, str>;
+ /// # }
+ ///
+ /// let p1: DataPayload<HelloWorldV1Marker> =
+ /// DataPayload::from_owned(HelloWorldV1 {
+ /// message: Cow::Borrowed("Hello World"),
+ /// });
+ ///
+ /// assert_eq!("Hello World", p1.get().message);
+ ///
+ /// let string_to_append = "Extra";
+ /// let p2: DataPayload<HelloWorldV1MessageMarker> =
+ /// p1.try_map_project(|mut obj, _| {
+ /// if obj.message.is_empty() {
+ /// return Err("Example error");
+ /// }
+ /// obj.message.to_mut().push_str(string_to_append);
+ /// Ok(obj.message)
+ /// })?;
+ ///
+ /// assert_eq!("Hello WorldExtra", p2.get());
+ /// # Ok::<(), &'static str>(())
+ /// ```
+ #[allow(clippy::type_complexity)]
+ pub fn try_map_project<M2, F, E>(self, f: F) -> Result<DataPayload<M2>, E>
+ where
+ M2: DataMarker,
+ F: for<'a> FnOnce(
+ <M::Yokeable as Yokeable<'a>>::Output,
+ PhantomData<&'a ()>,
+ ) -> Result<<M2::Yokeable as Yokeable<'a>>::Output, E>,
+ M::Yokeable: zerofrom::ZeroFrom<'static, M::Yokeable>,
+ {
+ Ok(DataPayload(DataPayloadInner::Yoke(
+ match self.0 {
+ DataPayloadInner::Yoke(yoke) => yoke,
+ DataPayloadInner::StaticRef(r) => Yoke::new_owned(zerofrom::ZeroFrom::zero_from(r)),
+ }
+ .try_map_project(f)?,
+ )))
+ }
+
+ /// Version of [`DataPayload::map_project_cloned()`] that bubbles up an error from `f`.
+ ///
+ /// # Examples
+ ///
+ /// Same example as above, but bubble up an error:
+ ///
+ /// ```
+ /// // Same imports and definitions as above
+ /// # use icu_provider::hello_world::*;
+ /// # use icu_provider::prelude::*;
+ /// # use std::borrow::Cow;
+ /// # struct HelloWorldV1MessageMarker;
+ /// # impl DataMarker for HelloWorldV1MessageMarker {
+ /// # type Yokeable = Cow<'static, str>;
+ /// # }
+ ///
+ /// let p1: DataPayload<HelloWorldV1Marker> =
+ /// DataPayload::from_owned(HelloWorldV1 {
+ /// message: Cow::Borrowed("Hello World"),
+ /// });
+ ///
+ /// assert_eq!("Hello World", p1.get().message);
+ ///
+ /// let string_to_append = "Extra";
+ /// let p2: DataPayload<HelloWorldV1MessageMarker> = p1
+ /// .try_map_project_cloned(|obj, _| {
+ /// if obj.message.is_empty() {
+ /// return Err("Example error");
+ /// }
+ /// let mut message = obj.message.clone();
+ /// message.to_mut().push_str(string_to_append);
+ /// Ok(message)
+ /// })?;
+ ///
+ /// // Note: p1 is still valid, but the values no longer equal.
+ /// assert_ne!(p1.get().message, *p2.get());
+ /// assert_eq!("Hello WorldExtra", p2.get());
+ /// # Ok::<(), &'static str>(())
+ /// ```
+ #[allow(clippy::type_complexity)]
+ pub fn try_map_project_cloned<'this, M2, F, E>(&'this self, f: F) -> Result<DataPayload<M2>, E>
+ where
+ M2: DataMarker,
+ F: for<'a> FnOnce(
+ &'this <M::Yokeable as Yokeable<'a>>::Output,
+ PhantomData<&'a ()>,
+ ) -> Result<<M2::Yokeable as Yokeable<'a>>::Output, E>,
+ {
+ Ok(DataPayload(DataPayloadInner::Yoke(match &self.0 {
+ DataPayloadInner::Yoke(yoke) => yoke.try_map_project_cloned(f)?,
+ DataPayloadInner::StaticRef(r) => {
+ let output: <M2::Yokeable as Yokeable<'static>>::Output =
+ f(Yokeable::transform(*r), PhantomData)?;
+ // Safety: <M2::Yokeable as Yokeable<'static>>::Output is the same type as M2::Yokeable
+ Yoke::new_owned(unsafe { M2::Yokeable::make(output) })
+ }
+ })))
+ }
+
+ /// Convert between two [`DataMarker`] types that are compatible with each other
+ /// with compile-time type checking.
+ ///
+ /// This happens if they both have the same [`DataMarker::Yokeable`] type.
+ ///
+ /// Can be used to erase the key of a data payload in cases where multiple keys correspond
+ /// to the same data struct.
+ ///
+ /// For runtime dynamic casting, use [`DataPayload::dynamic_cast_mut()`].
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use icu_locid::locale;
+ /// use icu_provider::hello_world::*;
+ /// use icu_provider::prelude::*;
+ ///
+ /// struct CustomHelloWorldV1Marker;
+ /// impl DataMarker for CustomHelloWorldV1Marker {
+ /// type Yokeable = HelloWorldV1<'static>;
+ /// }
+ ///
+ /// let hello_world: DataPayload<HelloWorldV1Marker> = todo!();
+ /// let custom: DataPayload<CustomHelloWorldV1Marker> = hello_world.cast();
+ /// ```
+ #[inline]
+ pub fn cast<M2>(self) -> DataPayload<M2>
+ where
+ M2: DataMarker<Yokeable = M::Yokeable>,
+ {
+ DataPayload(match self.0 {
+ DataPayloadInner::Yoke(yoke) => DataPayloadInner::Yoke(yoke),
+ DataPayloadInner::StaticRef(r) => DataPayloadInner::StaticRef(r),
+ })
+ }
+
+ /// Convert a mutable reference of a [`DataPayload`] to another mutable reference
+ /// of the same type with runtime type checking.
+ ///
+ /// Primarily useful to convert from a generic to a concrete marker type.
+ ///
+ /// If the `M2` type argument does not match the true marker type, a `DataError` is returned.
+ ///
+ /// For compile-time static casting, use [`DataPayload::cast()`].
+ ///
+ /// # Examples
+ ///
+ /// Change the results of a particular request based on key:
+ ///
+ /// ```
+ /// use icu_locid::locale;
+ /// use icu_provider::hello_world::*;
+ /// use icu_provider::prelude::*;
+ ///
+ /// struct MyWrapper<P> {
+ /// inner: P,
+ /// }
+ ///
+ /// impl<M, P> DataProvider<M> for MyWrapper<P>
+ /// where
+ /// M: KeyedDataMarker,
+ /// P: DataProvider<M>,
+ /// {
+ /// #[inline]
+ /// fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ /// let mut res = self.inner.load(req)?;
+ /// if let Some(ref mut generic_payload) = res.payload {
+ /// let mut cast_result =
+ /// generic_payload.dynamic_cast_mut::<HelloWorldV1Marker>();
+ /// if let Ok(ref mut concrete_payload) = cast_result {
+ /// // Add an emoji to the hello world message
+ /// concrete_payload.with_mut(|data| {
+ /// data.message.to_mut().insert_str(0, "✨ ");
+ /// });
+ /// }
+ /// }
+ /// Ok(res)
+ /// }
+ /// }
+ ///
+ /// let provider = MyWrapper {
+ /// inner: HelloWorldProvider,
+ /// };
+ /// let formatter =
+ /// HelloWorldFormatter::try_new_unstable(&provider, &locale!("de").into())
+ /// .unwrap();
+ ///
+ /// assert_eq!(formatter.format_to_string(), "✨ Hallo Welt");
+ /// ```
+ #[inline]
+ pub fn dynamic_cast_mut<M2>(&mut self) -> Result<&mut DataPayload<M2>, DataError>
+ where
+ M2: DataMarker,
+ {
+ let this: &mut dyn core::any::Any = self;
+ if let Some(this) = this.downcast_mut() {
+ Ok(this)
+ } else {
+ Err(DataError::for_type::<M2>().with_str_context(core::any::type_name::<M>()))
+ }
+ }
+}
+
+impl DataPayload<BufferMarker> {
+ /// Converts an owned byte buffer into a `DataPayload<BufferMarker>`.
+ pub fn from_owned_buffer(buffer: Box<[u8]>) -> Self {
+ let yoke = Yoke::attach_to_cart(SelectedRc::new(buffer), |b| &**b);
+ // Safe because cart is wrapped
+ let yoke = unsafe { yoke.replace_cart(|b| Some(Cart(b))) };
+ Self(DataPayloadInner::Yoke(yoke))
+ }
+
+ /// Converts a yoked byte buffer into a `DataPayload<BufferMarker>`.
+ pub fn from_yoked_buffer(yoke: Yoke<&'static [u8], Option<Cart>>) -> Self {
+ Self(DataPayloadInner::Yoke(yoke))
+ }
+
+ /// Converts a static byte buffer into a `DataPayload<BufferMarker>`.
+ pub fn from_static_buffer(buffer: &'static [u8]) -> Self {
+ Self(DataPayloadInner::Yoke(Yoke::new_owned(buffer)))
+ }
+}
+
+impl<M> Default for DataPayload<M>
+where
+ M: DataMarker,
+ M::Yokeable: Default,
+{
+ fn default() -> Self {
+ Self::from_owned(Default::default())
+ }
+}
+
+/// A response object containing an object as payload and metadata about it.
+#[allow(clippy::exhaustive_structs)] // this type is stable
+pub struct DataResponse<M>
+where
+ M: DataMarker,
+{
+ /// Metadata about the returned object.
+ pub metadata: DataResponseMetadata,
+
+ /// The object itself; `None` if it was not loaded.
+ pub payload: Option<DataPayload<M>>,
+}
+
+impl<M> DataResponse<M>
+where
+ M: DataMarker,
+{
+ /// Takes ownership of the underlying payload. Error if not present.
+ ///
+ /// To take the metadata, too, use [`Self::take_metadata_and_payload()`].
+ #[inline]
+ pub fn take_payload(self) -> Result<DataPayload<M>, DataError> {
+ Ok(self.take_metadata_and_payload()?.1)
+ }
+
+ /// Takes ownership of the underlying metadata and payload. Error if payload is not present.
+ #[inline]
+ pub fn take_metadata_and_payload(
+ self,
+ ) -> Result<(DataResponseMetadata, DataPayload<M>), DataError> {
+ Ok((
+ self.metadata,
+ self.payload
+ .ok_or_else(|| DataErrorKind::MissingPayload.with_type_context::<M>())?,
+ ))
+ }
+}
+
+impl<M> TryFrom<DataResponse<M>> for DataPayload<M>
+where
+ M: DataMarker,
+{
+ type Error = DataError;
+
+ fn try_from(response: DataResponse<M>) -> Result<Self, Self::Error> {
+ response.take_payload()
+ }
+}
+
+impl<M> Debug for DataResponse<M>
+where
+ M: DataMarker,
+ for<'a> &'a <M::Yokeable as Yokeable<'a>>::Output: Debug,
+{
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ write!(
+ f,
+ "DataResponse {{ metadata: {:?}, payload: {:?} }}",
+ self.metadata, self.payload
+ )
+ }
+}
+
+/// Cloning a DataResponse is generally a cheap operation.
+/// See notes in the `Clone` impl for [`Yoke`].
+///
+/// # Examples
+///
+/// ```no_run
+/// use icu_provider::hello_world::*;
+/// use icu_provider::prelude::*;
+///
+/// let resp1: DataResponse<HelloWorldV1Marker> = todo!();
+/// let resp2 = resp1.clone();
+/// ```
+impl<M> Clone for DataResponse<M>
+where
+ M: DataMarker,
+ for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Clone,
+{
+ fn clone(&self) -> Self {
+ Self {
+ metadata: self.metadata.clone(),
+ payload: self.payload.clone(),
+ }
+ }
+}
+
+#[test]
+fn test_debug() {
+ use crate::hello_world::*;
+ use alloc::borrow::Cow;
+ let resp = DataResponse::<HelloWorldV1Marker> {
+ metadata: Default::default(),
+ payload: Some(DataPayload::from_owned(HelloWorldV1 {
+ message: Cow::Borrowed("foo"),
+ })),
+ };
+ assert_eq!("DataResponse { metadata: DataResponseMetadata { locale: None, buffer_format: None }, payload: Some(HelloWorldV1 { message: \"foo\" }) }", format!("{resp:?}"));
+}