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
use gc_arena::{Collect, MutationContext};

use crate::{
    flatten::Flatten,
    map::{Map, MapWith},
    then::{Then, ThenWith},
    Sequence,
};

/// Extension trait for `Sequence` that provides useful combinator methods.
pub trait SequenceExt<'gc>: Sized + Sequence<'gc> {
    /// Map a function over result of this sequence.
    ///
    /// The given function is run in the same call to `Sequence::step` that produces the result of
    /// this sequence.
    fn map<F, R>(self, f: F) -> Map<Self, F>
    where
        F: 'static + FnOnce(Self::Output) -> R,
    {
        Map::new(self, f)
    }

    /// Equivalent to `SequencExt::map` but calls the function with a context parameter.
    ///
    /// The context parameter can be anything that implements `Collect`.  It exists to allow
    /// closures to manually close over variables that implement `Collect`, because there is no way
    /// currently for closure types to automatically implement `Collect` and are thus required to be
    /// 'static.
    fn map_with<C, F, R>(self, c: C, f: F) -> MapWith<Self, C, F>
    where
        C: Collect,
        F: 'static + FnOnce(C, Self::Output) -> R,
    {
        MapWith::new(self, c, f)
    }

    /// Execute a separate sequence step after this sequence completes.
    ///
    /// The given function is run in a separate `Sequence::step` call from the one that produces the
    /// result of this sequence.
    fn then<F, R>(self, f: F) -> Then<'gc, Self, F>
    where
        Self::Output: Collect,
        F: 'static + FnOnce(MutationContext<'gc, '_>, Self::Output) -> R,
    {
        Then::new(self, f)
    }

    /// Equivalent to `SequenceExt::then` but calls the function with the given context parameter.
    ///
    /// The context parameter can be anything that implements `Collect`, it exists to allow closures
    /// to manually close over variables that implement `Collect`, because there is no way currently
    /// for closure types to automatically themselves implement `Collect` and are thus required to
    /// be 'static.
    fn then_with<C, F, R>(self, c: C, f: F) -> ThenWith<'gc, Self, C, F>
    where
        C: Collect,
        Self::Output: Collect,
        F: 'static + FnOnce(MutationContext<'gc, '_>, C, Self::Output) -> R,
    {
        ThenWith::new(self, c, f)
    }

    /// Call a function on the result of this sequence, producing a new sequence to run.
    ///
    /// The given function is run in a separate `Sequence::step` call to the one that produces a
    /// result of this sequence, and the `Sequence` that this function results in is run in
    /// additional separate `Sequence::step` calls.
    fn chain<F, R>(self, f: F) -> Flatten<'gc, Then<'gc, Self, F>>
    where
        Self::Output: Collect,
        F: 'static + FnOnce(MutationContext<'gc, '_>, Self::Output) -> R,
        R: Sequence<'gc>,
    {
        Flatten::new(Then::new(self, f))
    }

    /// Equivalent to `SequenceExt::chain` but calls the function with the given context parameter.
    ///
    /// The context parameter can be anything that implements `Collect`, it exists to allow closures
    /// to manually close over variables that implement `Collect`, because there is no way currently
    /// for closure types to automatically themselves implement `Collect` and are thus required to
    /// be 'static.
    fn chain_with<C, F, R>(self, c: C, f: F) -> Flatten<'gc, ThenWith<'gc, Self, C, F>>
    where
        C: Collect,
        Self::Output: Collect,
        F: 'static + FnOnce(MutationContext<'gc, '_>, C, Self::Output) -> R,
        R: Sequence<'gc>,
    {
        Flatten::new(ThenWith::new(self, c, f))
    }

    /// If this sequence results in another sequence, this combinator flattens them so that they are
    /// executed one after another.
    fn flatten(self) -> Flatten<'gc, Self>
    where
        Self::Output: Sequence<'gc>,
    {
        Flatten::new(self)
    }

    /// Turn this sequence into a boxed sequence type.
    ///
    /// The return type is a `dyn Sequence` because where you would need to produce a boxed sequence
    /// you generally are doing this to purposefully forget what the particular sequence type is,
    /// and doing this here eases type inference.
    fn boxed(self) -> Box<dyn Sequence<'gc, Output = Self::Output> + 'gc>
    where
        Self: 'gc,
    {
        Box::new(self)
    }
}

impl<'gc, T> SequenceExt<'gc> for T where T: Sequence<'gc> {}