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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use super::{LCons, LNil, TList};
use crate::{
    counter::{Counter, Current, Next},
    functional::{ApplyFunctor, Functor},
};
use std::marker::PhantomData;

// remove

/// Removes `Target` type from [TList].
///
/// The auxiliary `Index` argument is intended for
/// list traversal. It can be left unspecified and
/// the compiler will figure it out.
pub trait LRemoveAtOp<Target, Index>
where
    Index: Counter,
    Self: TList,
    Self::Output: TList,
{
    type Output;
}

impl<Target, Tail> LRemoveAtOp<Target, Current> for LCons<Target, Tail>
where
    Tail: TList,
{
    type Output = Tail;
}

impl<Target, Index, NonTarget, Tail> LRemoveAtOp<Target, Next<Index>> for LCons<NonTarget, Tail>
where
    Index: Counter,
    Tail: TList + LRemoveAtOp<Target, Index>,
{
    type Output = LCons<NonTarget, LRemoveAtOpOutput<Tail, Target, Index>>;
}

pub type LRemoveAtOpOutput<List, Target, Index> = <List as LRemoveAtOp<Target, Index>>::Output;

/// A [Functor] that removes `Target` from [TList].
pub struct LRemoveAtFunctor<Target, Index> {
    _phantom: PhantomData<(Target, Index)>,
}

pub type LRemoveAt<List, Target, Index> = ApplyFunctor<LRemoveAtFunctor<Target, Index>, List>;

impl<List, Target, Index> Functor<List> for LRemoveAtFunctor<Target, Index>
where
    List: TList + LRemoveAtOp<Target, Index>,
    Index: Counter,
{
    type Output = LRemoveAtOpOutput<List, Target, Index>;
}

// remove multiple items

/// Removes a collection of types from [TList].
///
/// The `Targets` argument accepts a [TList] of types to be removed.
/// The `Indexes` argument can be left unspecified.
pub trait LRemoveManyOp<Targets, Indexes>
where
    Targets: TList,
    Indexes: TList,
    Self: TList,
    Self::Output: TList,
{
    type Output;
}

impl<List> LRemoveManyOp<LNil, LNil> for List
where
    List: TList,
{
    type Output = List;
}

impl<Index, IRemain, Target, TRemain, Head, Tail>
    LRemoveManyOp<LCons<Target, TRemain>, LCons<Index, IRemain>> for LCons<Head, Tail>
where
    Index: Counter,
    IRemain: TList,
    TRemain: TList,
    Tail: TList,
    Self: LRemoveAtOp<Target, Index>,
    LRemoveAtOpOutput<Self, Target, Index>: LRemoveManyOp<TRemain, IRemain>,
{
    type Output = LRemoveManyOpOutput<LRemoveAtOpOutput<Self, Target, Index>, TRemain, IRemain>;
}

pub type LRemoveManyOpOutput<List, Targets, Indexes> =
    <List as LRemoveManyOp<Targets, Indexes>>::Output;

/// A [Functor] that removes multiple `Targets` in [TList].
pub struct LRemoveManyFunctor<Targets, Indexes>
where
    Targets: TList,
    Indexes: TList,
{
    _phantom: PhantomData<(Targets, Indexes)>,
}

pub type LRemoveMany<List, Targets, Indexes> =
    ApplyFunctor<LRemoveManyFunctor<Targets, Indexes>, List>;

impl<List, Targets, Indexes> Functor<List> for LRemoveManyFunctor<Targets, Indexes>
where
    List: TList + LRemoveManyOp<Targets, Indexes>,
    Targets: TList,
    Indexes: TList,
{
    type Output = LRemoveManyOpOutput<List, Targets, Indexes>;
}

// tests

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{control::IfSameOutput, TListType};

    type AssertSame<Lhs, Rhs> = IfSameOutput<(), Lhs, Rhs>;

    struct A;
    struct B;
    struct C;

    type SomeList = TListType![A, B, C];

    // remove
    type Assert7<Idx> = AssertSame<LRemoveAt<SomeList, B, Idx>, TListType![A, C]>;

    // remove multiple items
    type Assert8<Idx> = AssertSame<LRemoveMany<SomeList, TListType![A, C], Idx>, TListType![B]>;

    // remove until empty
    type Assert9<Idx> = AssertSame<LRemoveMany<SomeList, TListType![B, A, C], Idx>, TListType![]>;

    #[test]
    fn tlist_remove_test() {
        let _: Assert7<_> = ();
        let _: Assert8<_> = ();
        let _: Assert9<_> = ();
    }
}