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

/// Drain and filter at the same time, accepts a `mutable vector ref` and `predicate`.
/// The `predicate` receives a `vector element ref` for the current iteration and
/// in-place removes the element from the vector, if `predicate` returns `true`.
/// Returns and array of `removed` elements back.
/// 
/// ## Examples
/// 
/// ```
/// use cs_utils::{drain_filter, random_bool};
/// 
/// let mut test_vector = vec![
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
/// ];
/// 
/// let removed_items = drain_filter(
///     &mut test_vector,
///     |el| {
///         return *el;
///     },
/// );
/// 
/// assert!(
///     !removed_items.contains(&false),
///     "Removed items must not contain \"false\" values.",
/// );
/// 
/// assert!(
///     !test_vector.contains(&true),
///     "Removed items must not contain \"true\" values.",
/// );
/// ```
pub fn drain_filter<T>(vector: &mut Vec<T>, mut predicate: impl FnMut(&T) -> bool) -> Vec<T> {
    let mut result = vec![];

    let mut i = 0;
        while i < vector.len() {
            let item = &vector[i];
            if predicate(item) {
                let removed_item = vector.swap_remove(i);
                result.push(removed_item);
                // since we swapped with the last vector item, don't increment
                // the index, - the swapped item must be checked too
                continue;
            }

            i += 1;
        }

    return result;
}

#[cfg(test)]
mod tests {
    use crate::drain_filter;

    #[test]
    fn drains_the_vector() {
        let mut test_vector = vec![
            true,
            false,
            false,
            true,
            true,
            false,
            false,
            false,
        ];

        let removed = drain_filter(&mut test_vector, |el| {
            return *el;
        });

        assert_eq!(
            removed.len(),
            3,
            "Must remove all elements equal to \"true\"."
        );

        assert!(
            !removed.contains(&false),
            "Removed elements vector must not contain \"false\" values."
        );

        assert_eq!(
            test_vector.len(),
            5,
            "Original vector must retain \"false\" values."
        );

        assert!(
            !test_vector.contains(&true),
            "Original vector must not contain \"true\" values."
        );
    }

    #[test]
    fn works_with_non_clone_items() {
        struct TestStruct {
            pub flag: bool,
        }

        let mut test_vector = vec![
            TestStruct { flag: false },
            TestStruct { flag: true },
            TestStruct { flag: false },
            TestStruct { flag: true },
            TestStruct { flag: false },
            TestStruct { flag: true },
            TestStruct { flag: false },
            TestStruct { flag: true },
            TestStruct { flag: true },
        ];

        let removed = drain_filter(&mut test_vector, |el| {
            return el.flag;
        });

        assert_eq!(
            removed.len(),
            5,
            "Must remove all elements equal to \"true\"."
        );

        assert_eq!(
            test_vector.len(),
            4,
            "Must retain all elements equal to \"false\"."
        );
    }
}