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;
#[derive(Debug, Clone, PartialEq, Default)]
#[non_exhaustive]
pub struct DataResponseMetadata {
pub locale: Option<DataLocale>,
pub buffer_format: Option<crate::buf::BufferFormat>,
}
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),
}
#[derive(Clone, Debug)]
#[allow(clippy::redundant_allocation)] pub struct Cart(SelectedRc<Box<[u8]>>);
impl Deref for Cart {
type Target = Box<[u8]>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
unsafe impl stable_deref_trait::StableDeref for Cart {}
unsafe impl yoke::CloneableCart for Cart {}
impl Cart {
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)
}
}
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)
}
}
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,
{
#[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))
}
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"))
}
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!(),
}
}
#[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),
}
}
#[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),
))
}
#[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);
let yokeable: M2::Yokeable = unsafe { M2::Yokeable::make(output) };
Yoke::new_owned(yokeable)
}
}))
}
#[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)?,
)))
}
#[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)?;
Yoke::new_owned(unsafe { M2::Yokeable::make(output) })
}
})))
}
#[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),
})
}
#[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> {
pub fn from_owned_buffer(buffer: Box<[u8]>) -> Self {
let yoke = Yoke::attach_to_cart(SelectedRc::new(buffer), |b| &**b);
let yoke = unsafe { yoke.replace_cart(|b| Some(Cart(b))) };
Self(DataPayloadInner::Yoke(yoke))
}
pub fn from_yoked_buffer(yoke: Yoke<&'static [u8], Option<Cart>>) -> Self {
Self(DataPayloadInner::Yoke(yoke))
}
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())
}
}
#[allow(clippy::exhaustive_structs)] pub struct DataResponse<M>
where
M: DataMarker,
{
pub metadata: DataResponseMetadata,
pub payload: Option<DataPayload<M>>,
}
impl<M> DataResponse<M>
where
M: DataMarker,
{
#[inline]
pub fn take_payload(self) -> Result<DataPayload<M>, DataError> {
Ok(self.take_metadata_and_payload()?.1)
}
#[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
)
}
}
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:?}"));
}