use crate::either::EitherCart;
#[cfg(feature = "alloc")]
use crate::erased::{ErasedArcCart, ErasedBoxCart, ErasedRcCart};
use crate::trait_hack::YokeTraitHack;
use crate::Yokeable;
use core::marker::PhantomData;
use core::ops::Deref;
use stable_deref_trait::StableDeref;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::rc::Rc;
#[cfg(feature = "alloc")]
use alloc::sync::Arc;
pub struct Yoke<Y: for<'a> Yokeable<'a>, C> {
yokeable: Y,
cart: C,
}
impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, C>
where
<C as Deref>::Target: 'static,
{
pub fn attach_to_cart<F>(cart: C, f: F) -> Self
where
F: for<'de> FnOnce(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
<C as Deref>::Target: 'static,
{
let deserialized = f(cart.deref());
Self {
yokeable: unsafe { Y::make(deserialized) },
cart,
}
}
pub fn try_attach_to_cart<E, F>(cart: C, f: F) -> Result<Self, E>
where
F: for<'de> FnOnce(&'de <C as Deref>::Target) -> Result<<Y as Yokeable<'de>>::Output, E>,
{
let deserialized = f(cart.deref())?;
Ok(Self {
yokeable: unsafe { Y::make(deserialized) },
cart,
})
}
#[deprecated]
pub fn attach_to_cart_badly(
cart: C,
f: for<'de> fn(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
) -> Self {
Self::attach_to_cart(cart, f)
}
#[deprecated]
pub fn try_attach_to_cart_badly<E>(
cart: C,
f: for<'de> fn(&'de <C as Deref>::Target) -> Result<<Y as Yokeable<'de>>::Output, E>,
) -> Result<Self, E> {
Self::try_attach_to_cart(cart, f)
}
}
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
#[inline]
pub fn get<'a>(&'a self) -> &'a <Y as Yokeable<'a>>::Output {
self.yokeable.transform()
}
pub fn backing_cart(&self) -> &C {
&self.cart
}
pub fn into_backing_cart(self) -> C {
self.cart
}
#[inline]
pub unsafe fn replace_cart<C2>(self, f: impl FnOnce(C) -> C2) -> Yoke<Y, C2> {
Yoke {
yokeable: self.yokeable,
cart: f(self.cart),
}
}
pub fn with_mut<'a, F>(&'a mut self, f: F)
where
F: 'static + for<'b> FnOnce(&'b mut <Y as Yokeable<'a>>::Output),
{
self.yokeable.transform_mut(f)
}
#[inline]
pub fn wrap_cart_in_option(self) -> Yoke<Y, Option<C>> {
unsafe {
self.replace_cart(Some)
}
}
}
impl<Y: for<'a> Yokeable<'a>> Yoke<Y, ()> {
pub fn new_always_owned(yokeable: Y) -> Self {
Self { yokeable, cart: () }
}
pub fn into_yokeable(self) -> Y {
self.yokeable
}
}
impl<Y: for<'a> Yokeable<'a>, C: StableDeref> Yoke<Y, Option<C>> {
pub fn new_owned(yokeable: Y) -> Self {
Self {
yokeable,
cart: None,
}
}
pub fn try_into_yokeable(self) -> Result<Y, Self> {
match self.cart {
Some(_) => Err(self),
None => Ok(self.yokeable),
}
}
}
pub unsafe trait CloneableCart: Clone {}
#[cfg(feature = "alloc")]
unsafe impl<T: ?Sized> CloneableCart for Rc<T> {}
#[cfg(feature = "alloc")]
unsafe impl<T: ?Sized> CloneableCart for Arc<T> {}
unsafe impl<T: CloneableCart> CloneableCart for Option<T> {}
unsafe impl<'a, T: ?Sized> CloneableCart for &'a T {}
unsafe impl CloneableCart for () {}
impl<Y: for<'a> Yokeable<'a>, C: CloneableCart> Clone for Yoke<Y, C>
where
for<'a> YokeTraitHack<<Y as Yokeable<'a>>::Output>: Clone,
{
fn clone(&self) -> Self {
let this: &Y::Output = self.get();
let this_hack = YokeTraitHack(this).into_ref();
Yoke {
yokeable: unsafe { Y::make(this_hack.clone().0) },
cart: self.cart.clone(),
}
}
}
#[test]
fn test_clone() {
let local_data = "foo".to_owned();
let y1 = Yoke::<alloc::borrow::Cow<'static, str>, Rc<String>>::attach_to_zero_copy_cart(
Rc::new(local_data),
);
let y2 = y1.clone();
assert_eq!(y1.get(), "foo");
assert_eq!(y2.get(), "foo");
let mut y3 = y1.clone();
y3.with_mut(|y| {
y.to_mut().push_str("bar");
});
assert_eq!(y1.get(), "foo");
assert_eq!(y2.get(), "foo");
assert_eq!(y3.get(), "foobar");
let y4 = y3.clone();
y3.with_mut(|y| {
y.to_mut().push_str("baz");
});
assert_eq!(y1.get(), "foo");
assert_eq!(y2.get(), "foo");
assert_eq!(y3.get(), "foobarbaz");
assert_eq!(y4.get(), "foobar");
}
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
pub fn map_project<P, F>(self, f: F) -> Yoke<P, C>
where
P: for<'a> Yokeable<'a>,
F: for<'a> FnOnce(
<Y as Yokeable<'a>>::Output,
PhantomData<&'a ()>,
) -> <P as Yokeable<'a>>::Output,
{
let p = f(self.yokeable.transform_owned(), PhantomData);
Yoke {
yokeable: unsafe { P::make(p) },
cart: self.cart,
}
}
pub fn map_project_cloned<'this, P, F>(&'this self, f: F) -> Yoke<P, C>
where
P: for<'a> Yokeable<'a>,
C: CloneableCart,
F: for<'a> FnOnce(
&'this <Y as Yokeable<'a>>::Output,
PhantomData<&'a ()>,
) -> <P as Yokeable<'a>>::Output,
{
let p = f(self.get(), PhantomData);
Yoke {
yokeable: unsafe { P::make(p) },
cart: self.cart.clone(),
}
}
pub fn try_map_project<P, F, E>(self, f: F) -> Result<Yoke<P, C>, E>
where
P: for<'a> Yokeable<'a>,
F: for<'a> FnOnce(
<Y as Yokeable<'a>>::Output,
PhantomData<&'a ()>,
) -> Result<<P as Yokeable<'a>>::Output, E>,
{
let p = f(self.yokeable.transform_owned(), PhantomData)?;
Ok(Yoke {
yokeable: unsafe { P::make(p) },
cart: self.cart,
})
}
pub fn try_map_project_cloned<'this, P, F, E>(&'this self, f: F) -> Result<Yoke<P, C>, E>
where
P: for<'a> Yokeable<'a>,
C: CloneableCart,
F: for<'a> FnOnce(
&'this <Y as Yokeable<'a>>::Output,
PhantomData<&'a ()>,
) -> Result<<P as Yokeable<'a>>::Output, E>,
{
let p = f(self.get(), PhantomData)?;
Ok(Yoke {
yokeable: unsafe { P::make(p) },
cart: self.cart.clone(),
})
}
pub fn map_project_with_explicit_capture<P, T>(
self,
capture: T,
f: for<'a> fn(
<Y as Yokeable<'a>>::Output,
capture: T,
PhantomData<&'a ()>,
) -> <P as Yokeable<'a>>::Output,
) -> Yoke<P, C>
where
P: for<'a> Yokeable<'a>,
{
let p = f(self.yokeable.transform_owned(), capture, PhantomData);
Yoke {
yokeable: unsafe { P::make(p) },
cart: self.cart,
}
}
pub fn map_project_cloned_with_explicit_capture<'this, P, T>(
&'this self,
capture: T,
f: for<'a> fn(
&'this <Y as Yokeable<'a>>::Output,
capture: T,
PhantomData<&'a ()>,
) -> <P as Yokeable<'a>>::Output,
) -> Yoke<P, C>
where
P: for<'a> Yokeable<'a>,
C: CloneableCart,
{
let p = f(self.get(), capture, PhantomData);
Yoke {
yokeable: unsafe { P::make(p) },
cart: self.cart.clone(),
}
}
#[allow(clippy::type_complexity)]
pub fn try_map_project_with_explicit_capture<P, T, E>(
self,
capture: T,
f: for<'a> fn(
<Y as Yokeable<'a>>::Output,
capture: T,
PhantomData<&'a ()>,
) -> Result<<P as Yokeable<'a>>::Output, E>,
) -> Result<Yoke<P, C>, E>
where
P: for<'a> Yokeable<'a>,
{
let p = f(self.yokeable.transform_owned(), capture, PhantomData)?;
Ok(Yoke {
yokeable: unsafe { P::make(p) },
cart: self.cart,
})
}
#[allow(clippy::type_complexity)]
pub fn try_map_project_cloned_with_explicit_capture<'this, P, T, E>(
&'this self,
capture: T,
f: for<'a> fn(
&'this <Y as Yokeable<'a>>::Output,
capture: T,
PhantomData<&'a ()>,
) -> Result<<P as Yokeable<'a>>::Output, E>,
) -> Result<Yoke<P, C>, E>
where
P: for<'a> Yokeable<'a>,
C: CloneableCart,
{
let p = f(self.get(), capture, PhantomData)?;
Ok(Yoke {
yokeable: unsafe { P::make(p) },
cart: self.cart.clone(),
})
}
}
#[cfg(feature = "alloc")]
impl<Y: for<'a> Yokeable<'a>, C: 'static + Sized> Yoke<Y, Rc<C>> {
pub fn erase_rc_cart(self) -> Yoke<Y, ErasedRcCart> {
unsafe {
self.replace_cart(|c| c as ErasedRcCart)
}
}
}
#[cfg(feature = "alloc")]
impl<Y: for<'a> Yokeable<'a>, C: 'static + Sized + Send + Sync> Yoke<Y, Arc<C>> {
pub fn erase_arc_cart(self) -> Yoke<Y, ErasedArcCart> {
unsafe {
self.replace_cart(|c| c as ErasedArcCart)
}
}
}
#[cfg(feature = "alloc")]
impl<Y: for<'a> Yokeable<'a>, C: 'static + Sized> Yoke<Y, Box<C>> {
pub fn erase_box_cart(self) -> Yoke<Y, ErasedBoxCart> {
unsafe {
self.replace_cart(|c| c as ErasedBoxCart)
}
}
}
#[cfg(feature = "alloc")]
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
#[inline]
pub fn wrap_cart_in_box(self) -> Yoke<Y, Box<C>> {
unsafe {
self.replace_cart(Box::new)
}
}
#[inline]
pub fn wrap_cart_in_rc(self) -> Yoke<Y, Rc<C>> {
unsafe {
self.replace_cart(Rc::new)
}
}
#[inline]
pub fn wrap_cart_in_arc(self) -> Yoke<Y, Arc<C>> {
unsafe {
self.replace_cart(Arc::new)
}
}
}
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
#[inline]
pub fn wrap_cart_in_either_a<B>(self) -> Yoke<Y, EitherCart<C, B>> {
unsafe {
self.replace_cart(EitherCart::A)
}
}
#[inline]
pub fn wrap_cart_in_either_b<A>(self) -> Yoke<Y, EitherCart<A, C>> {
unsafe {
self.replace_cart(EitherCart::B)
}
}
}
const _: () = ();
const _: () = ();