aboutsummaryrefslogtreecommitdiff
path: root/src/iter.rs
blob: 4b6210e28cb4b35b0745292c57cb998d83f68611 (plain)
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
//! Iterating over set flag values.

use crate::{Flags, Flag};

/// An iterator over a set of flags.
///
/// Any bits that don't correspond to a valid flag will be yielded
/// as a final item from the iterator.
pub struct Iter<B: 'static> {
    inner: IterNames<B>,
    done: bool,
}

impl<B: Flags> Iter<B> {
    /// Create a new iterator over the given set of flags.
    pub(crate) fn new(flags: &B) -> Self {
        Iter {
            inner: IterNames::new(flags),
            done: false,
        }
    }
}

impl<B: 'static> Iter<B> {
    #[doc(hidden)]
    pub const fn __private_const_new(flags: &'static [Flag<B>], source: B, state: B) -> Self {
        Iter {
            inner: IterNames::__private_const_new(flags, source, state),
            done: false,
        }
    }
}

impl<B: Flags> Iterator for Iter<B> {
    type Item = B;
    
    fn next(&mut self) -> Option<Self::Item> {
        match self.inner.next() {
            Some((_, flag)) => Some(flag),
            None if !self.done => {
                self.done = true;
                
                // After iterating through valid names, if there are any bits left over
                // then return one final value that includes them. This makes `into_iter`
                // and `from_iter` roundtrip
                if !self.inner.remaining().is_empty() {
                    Some(B::from_bits_retain(self.inner.state.bits()))
                } else {
                    None
                }
            }
            None => None,
        }
    }
}

/// An iterator over a set of flags and their names.
///
/// Any bits that don't correspond to a valid flag will be ignored.
pub struct IterNames<B: 'static> {
    flags: &'static [Flag<B>],
    idx: usize,
    source: B,
    state: B,
}

impl<B: Flags> IterNames<B> {
    /// Create a new iterator over the given set of flags.
    pub(crate) fn new(flags: &B) -> Self {
        IterNames {
            flags: B::FLAGS,
            idx: 0,
            state: B::from_bits_retain(flags.bits()),
            source: B::from_bits_retain(flags.bits()),
        }
    }
}

impl<B: 'static> IterNames<B> {
    #[doc(hidden)]
    pub const fn __private_const_new(flags: &'static [Flag<B>], source: B, state: B) -> Self {
        IterNames {
            flags,
            idx: 0,
            state,
            source,
        }
    }

    /// Get the remaining (unyielded) flags.
    ///
    /// Once the iterator has finished, this method can be used to
    /// check whether or not there are any bits that didn't correspond
    /// to a valid flag remaining.
    pub fn remaining(&self) -> &B {
        &self.state
    }
}

impl<B: Flags> Iterator for IterNames<B> {
    type Item = (&'static str, B);
    
    fn next(&mut self) -> Option<Self::Item> {
        while let Some(flag) = self.flags.get(self.idx) {
            // Short-circuit if our state is empty
            if self.state.is_empty() {
                return None;
            }

            self.idx += 1;

            let bits = flag.value().bits();

            // NOTE: We check whether the flag exists in self, but remove it from
            // a different value. This ensure that overlapping flags are handled
            // properly. Take the following example:
            //
            // const A: 0b00000001;
            // const B: 0b00000101;
            //
            // Given the bits 0b00000101, both A and B are set. But if we removed A
            // as we encountered it we'd be left with 0b00000100, which doesn't
            // correspond to a valid flag on its own.
            if self.source.contains(B::from_bits_retain(bits)) {
                self.state.remove(B::from_bits_retain(bits));

                return Some((flag.name(), B::from_bits_retain(bits)));
            }
        }
        
        None
    }
}