use crate::buf::BufferMarker;
use crate::DataError;
use crate::DataLocale;
use crate::DynamicDataMarker;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use core::fmt::Debug;
use core::marker::PhantomData;
#[cfg(feature = "alloc")]
use core::ops::Deref;
use yoke::cartable_ptr::CartableOptionPointer;
use yoke::*;
#[cfg(feature = "alloc")]
#[cfg(not(feature = "sync"))]
use alloc::rc::Rc as SelectedRc;
#[cfg(feature = "alloc")]
#[cfg(feature = "sync")]
use alloc::sync::Arc as SelectedRc;
#[derive(Debug, Clone, PartialEq, Default)]
#[non_exhaustive]
pub struct DataResponseMetadata {
pub locale: Option<DataLocale>,
pub buffer_format: Option<crate::buf::BufferFormat>,
pub checksum: Option<u64>,
}
impl DataResponseMetadata {
pub fn with_checksum(self, checksum: u64) -> Self {
Self {
checksum: Some(checksum),
..self
}
}
}
pub struct DataPayload<M: DynamicDataMarker>(pub(crate) DataPayloadInner<M>);
pub struct DataPayloadOr<M: DynamicDataMarker, O>(pub(crate) DataPayloadOrInner<M, O>);
pub(crate) enum DataPayloadInner<M: DynamicDataMarker> {
Yoke(Yoke<M::DataStruct, CartableOptionPointer<CartInner>>),
StaticRef(&'static M::DataStruct),
}
pub(crate) enum DataPayloadOrInner<M: DynamicDataMarker, O> {
Yoke(Yoke<M::DataStruct, CartableOptionPointer<CartInner>>),
Inner(DataPayloadOrInnerInner<M, O>),
}
pub(crate) enum DataPayloadOrInnerInner<M: DynamicDataMarker, O> {
StaticRef(&'static M::DataStruct),
Other(O),
}
#[derive(Clone, Debug)]
pub struct Cart(#[allow(dead_code)] CartInner);
#[cfg(feature = "alloc")]
pub(crate) type CartInner = SelectedRc<Box<[u8]>>;
#[cfg(not(feature = "alloc"))]
pub(crate) type CartInner = &'static ();
unsafe impl CloneableCart for Cart {}
#[cfg(feature = "alloc")]
impl Deref for Cart {
type Target = Box<[u8]>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(feature = "alloc")]
unsafe impl stable_deref_trait::StableDeref for Cart {}
impl Cart {
#[cfg(feature = "alloc")]
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))
.map(|yoke| unsafe { yoke.replace_cart(Cart) })
.map(Yoke::wrap_cart_in_option)
}
#[inline]
pub(crate) fn unwrap_cart<Y>(yoke: Yoke<Y, Option<Cart>>) -> Yoke<Y, Option<CartInner>>
where
for<'a> Y: Yokeable<'a>,
{
unsafe { yoke.replace_cart(|option_cart| option_cart.map(|cart| cart.0)) }
}
}
impl<M> Debug for DataPayload<M>
where
M: DynamicDataMarker,
for<'a> &'a <M::DataStruct as Yokeable<'a>>::Output: Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.get().fmt(f)
}
}
impl<M, O> Debug for DataPayloadOr<M, O>
where
M: DynamicDataMarker,
for<'a> &'a <M::DataStruct as Yokeable<'a>>::Output: Debug,
O: Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.get()
.map(|v| Debug::fmt(&v, f))
.unwrap_or_else(|v| Debug::fmt(v, f))
}
}
impl<M> Clone for DataPayload<M>
where
M: DynamicDataMarker,
for<'a> <M::DataStruct 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, O> Clone for DataPayloadOr<M, O>
where
M: DynamicDataMarker,
for<'a> <M::DataStruct as Yokeable<'a>>::Output: Clone,
O: Clone,
{
fn clone(&self) -> Self {
Self(match &self.0 {
DataPayloadOrInner::Yoke(yoke) => DataPayloadOrInner::Yoke(yoke.clone()),
DataPayloadOrInner::Inner(DataPayloadOrInnerInner::StaticRef(r)) => {
DataPayloadOrInner::Inner(DataPayloadOrInnerInner::StaticRef(*r))
}
DataPayloadOrInner::Inner(DataPayloadOrInnerInner::Other(o)) => {
DataPayloadOrInner::Inner(DataPayloadOrInnerInner::Other(o.clone()))
}
})
}
}
impl<M> PartialEq for DataPayload<M>
where
M: DynamicDataMarker,
for<'a> <M::DataStruct as Yokeable<'a>>::Output: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.get() == other.get()
}
}
impl<M, O> PartialEq for DataPayloadOr<M, O>
where
M: DynamicDataMarker,
for<'a> <M::DataStruct as Yokeable<'a>>::Output: PartialEq,
O: Eq,
{
fn eq(&self, other: &Self) -> bool {
match (self.get(), other.get()) {
(Ok(x), Ok(y)) => x == y,
(Err(x), Err(y)) => x == y,
_ => false,
}
}
}
impl<M> Eq for DataPayload<M>
where
M: DynamicDataMarker,
for<'a> <M::DataStruct as Yokeable<'a>>::Output: Eq,
{
}
impl<M, O> Eq for DataPayloadOr<M, O>
where
M: DynamicDataMarker,
for<'a> <M::DataStruct as Yokeable<'a>>::Output: Eq,
O: Eq,
{
}
#[test]
fn test_clone_eq() {
use crate::hello_world::*;
let p1 = DataPayload::<HelloWorldV1>::from_static_str("Demo");
let p2 = p1.clone();
assert_eq!(p1, p2);
let p1 = DataPayloadOr::<HelloWorldV1, usize>::from_payload(p1);
let p2 = p1.clone();
assert_eq!(p1, p2);
let p3 = DataPayloadOr::<HelloWorldV1, usize>::from_other(555);
let p4 = p3.clone();
assert_eq!(p3, p4);
let p5 = DataPayloadOr::<HelloWorldV1, usize>::from_other(666);
assert_ne!(p3, p5);
assert_ne!(p4, p5);
assert_ne!(p1, p3);
assert_ne!(p1, p4);
assert_ne!(p1, p5);
assert_ne!(p2, p3);
assert_ne!(p2, p4);
assert_ne!(p2, p5);
}
impl<M> DataPayload<M>
where
M: DynamicDataMarker,
{
#[inline]
pub fn from_owned(data: M::DataStruct) -> Self {
Self(DataPayloadInner::Yoke(
Yoke::new_owned(data).convert_cart_into_option_pointer(),
))
}
#[inline]
pub const fn from_static_ref(data: &'static M::DataStruct) -> Self {
Self(DataPayloadInner::StaticRef(data))
}
pub fn with_mut<'a, F>(&'a mut self, f: F)
where
F: 'static + for<'b> FnOnce(&'b mut <M::DataStruct as Yokeable<'a>>::Output),
M::DataStruct: zerofrom::ZeroFrom<'static, M::DataStruct>,
{
if let DataPayloadInner::StaticRef(r) = self.0 {
self.0 = DataPayloadInner::Yoke(
Yoke::new_owned(zerofrom::ZeroFrom::zero_from(r))
.convert_cart_into_option_pointer(),
);
}
match &mut self.0 {
DataPayloadInner::Yoke(yoke) => yoke.with_mut(f),
_ => unreachable!(),
}
}
#[inline]
pub fn get<'a>(&'a self) -> &'a <M::DataStruct as Yokeable<'a>>::Output {
match &self.0 {
DataPayloadInner::Yoke(yoke) => yoke.get(),
DataPayloadInner::StaticRef(r) => Yokeable::transform(*r),
}
}
#[inline]
pub fn get_static(&self) -> Option<&'static <M::DataStruct as Yokeable<'static>>::Output> {
match &self.0 {
DataPayloadInner::Yoke(_) => None,
DataPayloadInner::StaticRef(r) => Some(Yokeable::transform(*r)),
}
}
pub fn map_project<M2, F>(self, f: F) -> DataPayload<M2>
where
M2: DynamicDataMarker,
F: for<'a> FnOnce(
<M::DataStruct as Yokeable<'a>>::Output,
PhantomData<&'a ()>,
) -> <M2::DataStruct as Yokeable<'a>>::Output,
M::DataStruct: zerofrom::ZeroFrom<'static, M::DataStruct>,
{
DataPayload(DataPayloadInner::Yoke(
match self.0 {
DataPayloadInner::Yoke(yoke) => yoke,
DataPayloadInner::StaticRef(r) => Yoke::new_owned(zerofrom::ZeroFrom::zero_from(r))
.convert_cart_into_option_pointer(),
}
.map_project(f),
))
}
pub fn map_project_cloned<'this, M2, F>(&'this self, f: F) -> DataPayload<M2>
where
M2: DynamicDataMarker,
F: for<'a> FnOnce(
&'this <M::DataStruct as Yokeable<'a>>::Output,
PhantomData<&'a ()>,
) -> <M2::DataStruct as Yokeable<'a>>::Output,
{
DataPayload(DataPayloadInner::Yoke(match &self.0 {
DataPayloadInner::Yoke(yoke) => yoke.map_project_cloned(f),
DataPayloadInner::StaticRef(r) => {
let output: <M2::DataStruct as Yokeable<'static>>::Output =
f(Yokeable::transform(*r), PhantomData);
let yokeable: M2::DataStruct = unsafe { M2::DataStruct::make(output) };
Yoke::new_owned(yokeable).convert_cart_into_option_pointer()
}
}))
}
pub fn try_map_project<M2, F, E>(self, f: F) -> Result<DataPayload<M2>, E>
where
M2: DynamicDataMarker,
F: for<'a> FnOnce(
<M::DataStruct as Yokeable<'a>>::Output,
PhantomData<&'a ()>,
) -> Result<<M2::DataStruct as Yokeable<'a>>::Output, E>,
M::DataStruct: zerofrom::ZeroFrom<'static, M::DataStruct>,
{
Ok(DataPayload(DataPayloadInner::Yoke(
match self.0 {
DataPayloadInner::Yoke(yoke) => yoke,
DataPayloadInner::StaticRef(r) => Yoke::new_owned(zerofrom::ZeroFrom::zero_from(r))
.convert_cart_into_option_pointer(),
}
.try_map_project(f)?,
)))
}
pub fn try_map_project_cloned<'this, M2, F, E>(&'this self, f: F) -> Result<DataPayload<M2>, E>
where
M2: DynamicDataMarker,
F: for<'a> FnOnce(
&'this <M::DataStruct as Yokeable<'a>>::Output,
PhantomData<&'a ()>,
) -> Result<<M2::DataStruct 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::DataStruct as Yokeable<'static>>::Output =
f(Yokeable::transform(*r), PhantomData)?;
Yoke::new_owned(unsafe { M2::DataStruct::make(output) })
.convert_cart_into_option_pointer()
}
})))
}
#[inline]
pub fn cast<M2>(self) -> DataPayload<M2>
where
M2: DynamicDataMarker<DataStruct = M::DataStruct>,
{
DataPayload(match self.0 {
DataPayloadInner::Yoke(yoke) => DataPayloadInner::Yoke(yoke),
DataPayloadInner::StaticRef(r) => DataPayloadInner::StaticRef(r),
})
}
#[inline]
pub fn cast_ref<M2>(&self) -> &DataPayload<M2>
where
M2: DynamicDataMarker<DataStruct = M::DataStruct>,
{
unsafe { &*(self as *const DataPayload<M> as *const DataPayload<M2>) }
}
pub fn dynamic_cast<M2>(self) -> Result<DataPayload<M2>, DataError>
where
M2: DynamicDataMarker,
{
let mut option_self = Some(self);
let mut option_out = None::<DataPayload<M2>>;
if let Some(x) = (&mut option_out as &mut dyn core::any::Any).downcast_mut() {
core::mem::swap(&mut option_self, x);
debug_assert!(option_out.is_some());
if let Some(out) = option_out {
return Ok(out);
}
}
Err(DataError::for_type::<M2>().with_str_context(core::any::type_name::<M>()))
}
#[inline]
pub fn dynamic_cast_mut<M2>(&mut self) -> Result<&mut DataPayload<M2>, DataError>
where
M2: DynamicDataMarker,
{
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> {
#[cfg(feature = "alloc")]
pub fn from_owned_buffer(buffer: Box<[u8]>) -> Self {
let yoke = Yoke::attach_to_cart(SelectedRc::new(buffer), |b| &**b)
.wrap_cart_in_option()
.convert_cart_into_option_pointer();
Self(DataPayloadInner::Yoke(yoke))
}
pub fn from_yoked_buffer(yoke: Yoke<&'static [u8], Option<Cart>>) -> Self {
let yoke = Cart::unwrap_cart(yoke);
Self(DataPayloadInner::Yoke(
yoke.convert_cart_into_option_pointer(),
))
}
pub fn from_static_buffer(buffer: &'static [u8]) -> Self {
Self(DataPayloadInner::Yoke(
Yoke::new_owned(buffer).convert_cart_into_option_pointer(),
))
}
}
impl<M> Default for DataPayload<M>
where
M: DynamicDataMarker,
M::DataStruct: Default,
{
fn default() -> Self {
Self::from_owned(Default::default())
}
}
impl<M, O> DataPayloadOr<M, O>
where
M: DynamicDataMarker,
{
#[inline]
pub fn from_payload(payload: DataPayload<M>) -> Self {
match payload.0 {
DataPayloadInner::Yoke(yoke) => Self(DataPayloadOrInner::Yoke(yoke)),
DataPayloadInner::StaticRef(r) => Self(DataPayloadOrInner::Inner(
DataPayloadOrInnerInner::StaticRef(r),
)),
}
}
#[inline]
pub fn from_other(other: O) -> Self {
Self(DataPayloadOrInner::Inner(DataPayloadOrInnerInner::Other(
other,
)))
}
#[inline]
pub fn is_payload(&self) -> bool {
match &self.0 {
DataPayloadOrInner::Yoke(_) => true,
DataPayloadOrInner::Inner(DataPayloadOrInnerInner::StaticRef(_)) => true,
DataPayloadOrInner::Inner(DataPayloadOrInnerInner::Other(_)) => false,
}
}
#[inline]
pub fn get<'a>(&'a self) -> Result<&'a <M::DataStruct as Yokeable<'a>>::Output, &'a O> {
match &self.0 {
DataPayloadOrInner::Yoke(yoke) => Ok(yoke.get()),
DataPayloadOrInner::Inner(DataPayloadOrInnerInner::StaticRef(r)) => {
Ok(Yokeable::transform(*r))
}
DataPayloadOrInner::Inner(DataPayloadOrInnerInner::Other(o)) => Err(o),
}
}
#[inline]
pub fn into_inner(self) -> Result<DataPayload<M>, O> {
match self.0 {
DataPayloadOrInner::Yoke(yoke) => Ok(DataPayload(DataPayloadInner::Yoke(yoke))),
DataPayloadOrInner::Inner(DataPayloadOrInnerInner::StaticRef(r)) => {
Ok(DataPayload(DataPayloadInner::StaticRef(r)))
}
DataPayloadOrInner::Inner(DataPayloadOrInnerInner::Other(o)) => Err(o),
}
}
}
impl<M> DataPayloadOr<M, ()>
where
M: DynamicDataMarker,
{
#[inline]
pub fn none() -> Self {
Self::from_other(())
}
#[inline]
pub fn get_option<'a>(&'a self) -> Option<&'a <M::DataStruct as Yokeable<'a>>::Output> {
self.get().ok()
}
}
#[allow(clippy::exhaustive_structs)] pub struct DataResponse<M>
where
M: DynamicDataMarker,
{
pub metadata: DataResponseMetadata,
pub payload: DataPayload<M>,
}
impl<M> DataResponse<M>
where
M: DynamicDataMarker,
{
#[inline]
pub fn cast<M2>(self) -> DataResponse<M2>
where
M2: DynamicDataMarker<DataStruct = M::DataStruct>,
{
DataResponse {
metadata: self.metadata,
payload: self.payload.cast(),
}
}
#[inline]
pub fn dynamic_cast<M2>(self) -> Result<DataResponse<M2>, DataError>
where
M2: DynamicDataMarker,
{
Ok(DataResponse {
metadata: self.metadata,
payload: self.payload.dynamic_cast()?,
})
}
}
impl<M> Debug for DataResponse<M>
where
M: DynamicDataMarker,
for<'a> &'a <M::DataStruct 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
)
}
}
impl<M> Clone for DataResponse<M>
where
M: DynamicDataMarker,
for<'a> <M::DataStruct 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 crate::prelude::*;
let resp = HelloWorldProvider
.load(DataRequest {
id: DataIdentifierBorrowed::for_locale(&icu_locale_core::locale!("en").into()),
..Default::default()
})
.unwrap();
assert_eq!("DataResponse { metadata: DataResponseMetadata { locale: None, buffer_format: None, checksum: Some(1234) }, payload: HelloWorld { message: \"Hello World\" } }", format!("{resp:?}"));
}