refcapsule 0.1.0-beta1

Safely send references to other threads
Documentation
use super::{Capsule, CapsuleMut, Scope};

/// Something that can be encapsulated
///
/// This allows one type containing a reference type to be converted into a different type that
/// contains capsules. Most notably it isi implemented for `&T` to convert to a `Capsule<T>` and
/// for `&mut T` to convert to a `CapsuleMute<T>`. It is also implemented for tuples of up to 8
/// values, as long as all values implement `Encapsulate`, so that you can capture multiple
/// references at once.
pub trait Encapsulate<'env> {
    /// The encapsulated type
    /// This should either be a `Capsule`, `CapsuleMut`, or something which contains
    /// some combination of those two.
    type Encapsulated;

    /// Capture references from `self`, and convert them into capsules
    ///
    /// This should call [`Scope::encapsulate`] and [`Scope::encapsulate_mut`]
    /// on the supplied scope with references from `self` in order to construct the
    /// `Encapsulated` result. The resulting capsules will be tied to the lifetime of
    /// `scope`, and the call to [`super::with_encapsulated`] won't return until the
    /// capsules are dropped (even if they are passed to another thread).
    fn encapsulate(self, scope: &Scope<'env>) -> Self::Encapsulated;
}

impl<'env, T: ?Sized + Sync> Encapsulate<'env> for &'env T {
    type Encapsulated = Capsule<T>;

    fn encapsulate(self, scope: &Scope<'env>) -> Self::Encapsulated {
        scope.encapsulate(self)
    }
}

impl<'env, T: ?Sized + Sync> Encapsulate<'env> for &'env mut T {
    type Encapsulated = CapsuleMut<T>;

    fn encapsulate(self, scope: &Scope<'env>) -> Self::Encapsulated {
        scope.encapsulate_mut(self)
    }
}

macro_rules! encapsulate_tuples {
    // Stopping criteria (1-ary tuple)
    ($n:ident : $t:ident) => {
        encapsulate_tuples!(@impl $n:$t);
    };
    ($hn:ident : $ht:ident $($tn:ident : $tt:ident)+) => {
        encapsulate_tuples!(@impl $($tn:$tt)+);
        encapsulate_tuples!(@impl $hn:$ht $($tn:$tt)+);
    };
    (@impl $($n:ident:$T:ident)+) => {
        impl<'env, $($T: Encapsulate<'env>,)*> Encapsulate<'env> for ($($T,)+) {
            type Encapsulated = ($($T::Encapsulated,)+);

            fn encapsulate(self, scope: &Scope<'env>) -> Self::Encapsulated {
                let ($($n,)+) = self;
                ($($n.encapsulate(scope),)+)
            }
        }
    };
}

encapsulate_tuples!(a:A b:B c:C d:D e:E f:F g:G h:H);

// TODO: slice? (probably need a wrapper type to avoid conflicting with &T

impl<'env, T: Encapsulate<'env>, const N: usize> Encapsulate<'env> for [T; N] {
    type Encapsulated = [T::Encapsulated; N];

    fn encapsulate(self, scope: &Scope<'env>) -> Self::Encapsulated {
        self.map(|i| i.encapsulate(scope))
    }
}

/// Wrapper type for using a function to generate Capsules
///
/// For better type inference, it is usually best to use [`gen`] to create this.
///
/// # Example
///
/// ```
/// use std::thread;
/// use refcapsule::{with_encapsulated, encapsulate::gen};
///
/// let x: u32 = 4;
/// let s = "hello world";
///
/// with_encapsulated(gen(|sc| (sc.encapsulate(&x), sc.encapsulate(s))), |(x, s)| {
///     thread::spawn(move || {
///         assert_eq!(*x, 4);
///         assert_eq!(&*s, "hello world");
///     });
/// });
/// ```
pub struct Gen<F>(pub F);

/// Helper function to create a [Gen].
///
/// This is mostly useful to improve type inference when using a function
/// to encapsulate references.
pub fn gen<'env, F, T>(f: F) -> Gen<F>
where
    F: FnOnce(&Scope<'env>) -> T,
{
    Gen(f)
}

impl<'env, F, T> Encapsulate<'env> for Gen<F>
where
    F: FnOnce(&Scope<'env>) -> T,
{
    type Encapsulated = T;

    fn encapsulate(self, scope: &Scope<'env>) -> T {
        self.0(scope)
    }
}