1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

//! Types to enable polymorphic carts.

use crate::CloneableCart;
use crate::Yoke;
use crate::Yokeable;
use core::ops::Deref;
use stable_deref_trait::StableDeref;

/// A cart that can be one type or the other. Enables ergonomic polymorphic carts.
///
/// `EitherCart` enables yokes originating from different data sources and therefore
/// having different cart types to be merged into the same yoke type, but still being
/// able to recover the original cart type if necessary.
///
/// All relevant Cart traits are implemented for `EitherCart`, and carts can be
/// safely wrapped in an `EitherCart`.
///
/// Also see [`Yoke::erase_box_cart()`].
///
/// # Examples
///
/// ```
/// use yoke::Yoke;
/// use yoke::either::EitherCart;
/// use std::borrow::Cow;
/// use std::rc::Rc;
///
/// let y1: Yoke<
///     &'static str,
///     Rc<str>
/// > = Yoke::attach_to_zero_copy_cart(
///     "reference counted hello world".into()
/// );
///
/// let y2: Yoke<
///     &'static str,
///     &str
/// > = Yoke::attach_to_zero_copy_cart(
///     "borrowed hello world"
/// );
///
/// type CombinedYoke<'a> = Yoke<
///     &'static str,
///     EitherCart<Rc<str>, &'a str>
/// >;
///
/// // Both yokes can be combined into a single yoke type despite different carts
/// let y3: CombinedYoke = y1.wrap_cart_in_either_a();
/// let y4: CombinedYoke = y2.wrap_cart_in_either_b();
///
/// assert_eq!(*y3.get(), "reference counted hello world");
/// assert_eq!(*y4.get(), "borrowed hello world");
///
/// // The resulting yoke is cloneable if both cart types implement CloneableCart
/// let y5 = y4.clone();
/// assert_eq!(*y5.get(), "borrowed hello world");
/// ```
#[derive(Clone, PartialEq, Eq)]
pub enum EitherCart<C0, C1> {
    A(C0),
    B(C1),
}

impl<C0, C1, T> Deref for EitherCart<C0, C1>
where
    C0: Deref<Target = T>,
    C1: Deref<Target = T>,
{
    type Target = T;
    fn deref(&self) -> &T {
        use EitherCart::*;
        match self {
            A(a) => a.deref(),
            B(b) => b.deref(),
        }
    }
}

// Safe because both sub-types implement the trait.
unsafe impl<C0, C1, T> StableDeref for EitherCart<C0, C1>
where
    C0: StableDeref,
    C1: StableDeref,
    C0: Deref<Target = T>,
    C1: Deref<Target = T>,
{
}

// Safe because both sub-types implement the trait.
unsafe impl<C0, C1> CloneableCart for EitherCart<C0, C1>
where
    C0: CloneableCart,
    C1: CloneableCart,
{
}

impl<Y: for<'a> Yokeable<'a>, C> Yoke<Y, C> {
    /// Helper function allowing one to wrap the cart type in an [`EitherCart`].
    ///
    /// This function wraps the cart into the `A` variant. To wrap it into the
    /// `B` variant, use [`Self::wrap_cart_in_either_b()`].
    ///
    /// For an example, see [`EitherCart`].
    #[inline]
    pub fn wrap_cart_in_either_a<B>(self) -> Yoke<Y, EitherCart<C, B>> {
        unsafe {
            // safe because the cart is preserved, just wrapped
            self.replace_cart(EitherCart::A)
        }
    }
    /// Helper function allowing one to wrap the cart type in an [`EitherCart`].
    ///
    /// This function wraps the cart into the `B` variant. To wrap it into the
    /// `A` variant, use [`Self::wrap_cart_in_either_a()`].
    ///
    /// For an example, see [`EitherCart`].
    #[inline]
    pub fn wrap_cart_in_either_b<A>(self) -> Yoke<Y, EitherCart<A, C>> {
        unsafe {
            // safe because the cart is preserved, just wrapped
            self.replace_cart(EitherCart::B)
        }
    }
}