use crate::{AsIs, BorrowAsIs, Is, IsMut, Owned, ToOwned, ToOwnedMut};
use core::borrow::{Borrow, BorrowMut};
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::ops::{Add, BitAnd, BitOr, BitXor, Deref, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub};
use ref_ops::{
RefAdd, RefBitAnd, RefBitOr, RefBitXor, RefDiv, RefMul, RefMutAdd, RefMutBitAnd, RefMutBitOr,
RefMutBitXor, RefMutDiv, RefMutMul, RefMutNeg, RefMutNot, RefMutRem, RefMutShl, RefMutShr,
RefMutSub, RefNeg, RefNot, RefRem, RefShl, RefShr, RefSub,
};
#[derive(Debug)]
pub enum IsCow<'a, T>
where
T: ?Sized + ToOwned,
{
Owned(T::Owned),
Borrowed(&'a T),
}
impl<T> Default for IsCow<'_, T>
where
T: ?Sized + ToOwned,
T::Owned: Default,
{
fn default() -> Self {
IsCow::Owned(T::Owned::default())
}
}
impl<T> Deref for IsCow<'_, T>
where
T: ?Sized + ToOwned,
{
type Target = T;
fn deref(&self) -> &T {
match self {
IsCow::Owned(x) => x.borrow(),
IsCow::Borrowed(x) => x,
}
}
}
impl<T> Borrow<T> for IsCow<'_, T>
where
T: ?Sized + ToOwned,
{
fn borrow(&self) -> &T {
self
}
}
impl<T> Borrow<T> for &IsCow<'_, T>
where
T: ?Sized + ToOwned,
{
fn borrow(&self) -> &T {
self
}
}
impl<T> Borrow<T> for &mut IsCow<'_, T>
where
T: ?Sized + ToOwned,
{
fn borrow(&self) -> &T {
self
}
}
impl<T, U> PartialEq<Is<'_, U>> for IsCow<'_, T>
where
T: ?Sized + ToOwned + PartialEq<U>,
U: ?Sized + ToOwned,
{
fn eq(&self, other: &Is<'_, U>) -> bool {
**self == **other
}
}
impl<T, U> PartialEq<IsCow<'_, U>> for IsCow<'_, T>
where
T: ?Sized + ToOwned + PartialEq<U>,
U: ?Sized + ToOwned,
{
fn eq(&self, other: &IsCow<'_, U>) -> bool {
**self == **other
}
}
impl<T, U> PartialEq<IsMut<'_, U>> for IsCow<'_, T>
where
T: ?Sized + ToOwned + PartialEq<U>,
U: ?Sized + ToOwned,
{
fn eq(&self, other: &IsMut<'_, U>) -> bool {
**self == **other
}
}
impl<T> Eq for IsCow<'_, T> where T: ?Sized + ToOwned + Eq {}
impl<T, U> PartialOrd<Is<'_, U>> for IsCow<'_, T>
where
T: ?Sized + ToOwned + PartialOrd<U>,
U: ?Sized + ToOwned,
{
fn partial_cmp(&self, other: &Is<'_, U>) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<T, U> PartialOrd<IsCow<'_, U>> for IsCow<'_, T>
where
T: ?Sized + ToOwned + PartialOrd<U>,
U: ?Sized + ToOwned,
{
fn partial_cmp(&self, other: &IsCow<'_, U>) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<T, U> PartialOrd<IsMut<'_, U>> for IsCow<'_, T>
where
T: ?Sized + ToOwned + PartialOrd<U>,
U: ?Sized + ToOwned,
{
fn partial_cmp(&self, other: &IsMut<'_, U>) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<T> Ord for IsCow<'_, T>
where
T: ?Sized + ToOwned + Ord,
{
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl<T> Hash for IsCow<'_, T>
where
T: ?Sized + ToOwned + Hash,
{
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T> BorrowAsIs for IsCow<'_, T>
where
T: ?Sized + BorrowAsIs<Is = T> + ToOwned,
{
type Is = T;
fn borrow_or_clone<B: ?Sized>(&self) -> IsCow<'_, B>
where
T: Borrow<B>,
B: ToOwned<Owned = T::Owned>,
{
(**self).borrow_or_clone()
}
}
impl<T> AsIs for IsCow<'_, T>
where
T: ?Sized + BorrowAsIs<Is = T> + ToOwned,
{
fn as_is<'a>(self) -> Is<'a, T>
where
Self: 'a,
{
match self {
IsCow::Owned(x) => Is::Owned(x),
IsCow::Borrowed(x) => match x.borrow_or_clone() {
IsCow::Owned(x) => Is::Owned(x),
IsCow::Borrowed(_) => Is::Borrowed(x),
},
}
}
}
macro_rules! impl_unop {
($Op:ident, $op:ident, $RefOp:ident, $ref_op:ident, $RefMutOp:ident, $ref_mut_op:ident) => {
impl<T> $Op for IsCow<'_, T>
where
T: ?Sized + ToOwned + $RefOp,
T::Owned: $Op<Output = T::Output>,
{
type Output = T::Output;
fn $op(self) -> Self::Output {
match self {
IsCow::Owned(x) => x.$op(),
IsCow::Borrowed(x) => x.$ref_op(),
}
}
}
impl<T> $Op for &IsCow<'_, T>
where
T: ?Sized + ToOwned + $RefOp,
{
type Output = T::Output;
fn $op(self) -> Self::Output {
(**self).$ref_op()
}
}
impl<T> $Op for &mut IsCow<'_, T>
where
T: ?Sized + ToOwnedMut + $RefOp + $RefMutOp<Output = <T as $RefOp>::Output>,
{
type Output = <T as $RefOp>::Output;
fn $op(self) -> Self::Output {
match self {
IsCow::Owned(x) => x.borrow_mut().$ref_mut_op(),
IsCow::Borrowed(x) => x.$ref_op(),
}
}
}
};
}
impl_unop!(Neg, neg, RefNeg, ref_neg, RefMutNeg, ref_mut_neg);
impl_unop!(Not, not, RefNot, ref_not, RefMutNot, ref_mut_not);
macro_rules! impl_binop {
($Op:ident, $op:ident, $RefOp:ident, $ref_op:ident, $RefMutOp:ident, $ref_mut_op:ident) => {
impl<T, U, O> $Op<U> for IsCow<'_, T>
where
T: ?Sized
+ ToOwned
+ $RefOp<Owned<U>, Output = O>
+ for<'a> $RefOp<&'a U::Is, Output = O>
+ for<'a> $RefOp<&'a mut U::Is, Output = O>,
U: AsIs,
T::Owned: $Op<Owned<U>, Output = O>
+ for<'a> $Op<&'a U::Is, Output = O>
+ for<'a> $Op<&'a mut U::Is, Output = O>,
{
type Output = O;
fn $op(self, rhs: U) -> Self::Output {
match self {
IsCow::Owned(x) => match rhs.as_is() {
Is::Owned(y) => x.$op(y),
Is::Borrowed(y) => x.$op(y),
Is::MutBorrowed(y) => x.$op(y),
},
IsCow::Borrowed(x) => match rhs.as_is() {
Is::Owned(y) => x.$ref_op(y),
Is::Borrowed(y) => x.$ref_op(y),
Is::MutBorrowed(y) => x.$ref_op(y),
},
}
}
}
impl<T, U, O> $Op<U> for &IsCow<'_, T>
where
T: ?Sized
+ ToOwned
+ $RefOp<Owned<U>, Output = O>
+ for<'a> $RefOp<&'a U::Is, Output = O>
+ for<'a> $RefOp<&'a mut U::Is, Output = O>,
U: AsIs,
{
type Output = O;
fn $op(self, rhs: U) -> Self::Output {
match rhs.as_is() {
Is::Owned(y) => (**self).$ref_op(y),
Is::Borrowed(y) => (**self).$ref_op(y),
Is::MutBorrowed(y) => (**self).$ref_op(y),
}
}
}
impl<T, U, O> $Op<U> for &mut IsCow<'_, T>
where
T: ?Sized
+ ToOwnedMut
+ $RefOp<Owned<U>, Output = O>
+ for<'a> $RefOp<&'a U::Is, Output = O>
+ for<'a> $RefOp<&'a mut U::Is, Output = O>
+ $RefMutOp<Owned<U>, Output = O>
+ for<'a> $RefMutOp<&'a U::Is, Output = O>
+ for<'a> $RefMutOp<&'a mut U::Is, Output = O>,
U: AsIs,
{
type Output = O;
fn $op(self, rhs: U) -> Self::Output {
match self {
IsCow::Owned(x) => match rhs.as_is() {
Is::Owned(y) => x.borrow_mut().$ref_mut_op(y),
Is::Borrowed(y) => x.borrow_mut().$ref_mut_op(y),
Is::MutBorrowed(y) => x.borrow_mut().$ref_mut_op(y),
},
IsCow::Borrowed(x) => match rhs.as_is() {
Is::Owned(y) => x.$ref_op(y),
Is::Borrowed(y) => x.$ref_op(y),
Is::MutBorrowed(y) => x.$ref_op(y),
},
}
}
}
};
}
impl_binop!(Add, add, RefAdd, ref_add, RefMutAdd, ref_mut_add);
impl_binop!(Sub, sub, RefSub, ref_sub, RefMutSub, ref_mut_sub);
impl_binop!(Mul, mul, RefMul, ref_mul, RefMutMul, ref_mut_mul);
impl_binop!(Div, div, RefDiv, ref_div, RefMutDiv, ref_mut_div);
impl_binop!(Rem, rem, RefRem, ref_rem, RefMutRem, ref_mut_rem);
impl_binop!(Shl, shl, RefShl, ref_shl, RefMutShl, ref_mut_shl);
impl_binop!(Shr, shr, RefShr, ref_shr, RefMutShr, ref_mut_shr);
impl_binop!(
BitAnd,
bitand,
RefBitAnd,
ref_bitand,
RefMutBitAnd,
ref_mut_bitand
);
impl_binop!(
BitOr,
bitor,
RefBitOr,
ref_bitor,
RefMutBitOr,
ref_mut_bitor
);
impl_binop!(
BitXor,
bitxor,
RefBitXor,
ref_bitxor,
RefMutBitXor,
ref_mut_bitxor
);