use crate::trait_hack::YokeTraitHack;
use crate::IsCovariant;
use crate::Yokeable;
use core::marker::PhantomData;
use core::ops::Deref;
use stable_deref_trait::StableDeref;
#[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> {
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,
{
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,
})
}
pub fn attach_to_cart_badly(
cart: C,
f: for<'de> fn(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
) -> Self {
let deserialized = f(cart.deref());
Self {
yokeable: unsafe { Y::make(deserialized) },
cart,
}
}
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> {
let deserialized = f(cart.deref())?;
Ok(Self {
yokeable: unsafe { Y::make(deserialized) },
cart,
})
}
}
impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
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
}
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)
}
}
impl<Y: for<'a> Yokeable<'a>> Yoke<Y, ()> {
pub fn new_always_owned(yokeable: Y) -> Self {
Self { yokeable, cart: () }
}
}
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 attach_to_option_cart<F>(cart: C, f: F) -> Self
where
F: for<'de> FnOnce(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
{
let deserialized = f(cart.deref());
Self {
yokeable: unsafe { Y::make(deserialized) },
cart: Some(cart),
}
}
pub fn attach_to_option_cart_badly(
cart: C,
f: for<'de> fn(&'de <C as Deref>::Target) -> <Y as Yokeable<'de>>::Output,
) -> Self {
let deserialized = f(cart.deref());
Self {
yokeable: unsafe { Y::make(deserialized) },
cart: Some(cart),
}
}
}
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(),
}
}
}
unsafe impl<'b, Y: for<'a> Yokeable<'a>, C: IsCovariant<'b>> IsCovariant<'b> for Yoke<Y, C> {}
#[test]
fn test_clone() {
let local_data = "foo".to_string();
let y1 = Yoke::<alloc::borrow::Cow<'static, str>, Rc<String>>::attach_to_rc_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 project<P>(
self,
f: for<'a> fn(
<Y as Yokeable<'a>>::Output,
PhantomData<&'a ()>,
) -> <P as Yokeable<'a>>::Output,
) -> Yoke<P, C>
where
P: for<'a> Yokeable<'a>,
{
let p = f(self.yokeable.transform_owned(), PhantomData);
Yoke {
yokeable: unsafe { P::make(p) },
cart: self.cart,
}
}
pub fn project_cloned<'this, P>(
&'this self,
f: for<'a> fn(
&'this <Y as Yokeable<'a>>::Output,
PhantomData<&'a ()>,
) -> <P as Yokeable<'a>>::Output,
) -> Yoke<P, C>
where
P: for<'a> Yokeable<'a>,
C: CloneableCart,
{
let p = f(self.get(), PhantomData);
Yoke {
yokeable: unsafe { P::make(p) },
cart: self.cart.clone(),
}
}
pub fn project_with_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 project_cloned_with_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_project_with_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_project_cloned_with_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(),
})
}
}
const _: () = ();