bitflags/
iter.rs

1/*!
2Yield the bits of a source flags value in a set of contained flags values.
3*/
4
5use crate::{Flag, Flags};
6
7/**
8An iterator over flags values.
9
10This iterator will yield flags values for contained, defined flags first, with any remaining bits yielded
11as a final flags value.
12*/
13pub struct Iter<B: 'static> {
14    inner: IterNames<B>,
15    done: bool,
16}
17
18impl<B: Flags> Iter<B> {
19    pub(crate) fn new(flags: &B) -> Self {
20        Iter {
21            inner: IterNames::new(flags),
22            done: false,
23        }
24    }
25}
26
27impl<B: 'static> Iter<B> {
28    // Used by the `bitflags` macro
29    #[doc(hidden)]
30    pub const fn __private_const_new(flags: &'static [Flag<B>], source: B, remaining: B) -> Self {
31        Iter {
32            inner: IterNames::__private_const_new(flags, source, remaining),
33            done: false,
34        }
35    }
36}
37
38impl<B: Flags> Iterator for Iter<B> {
39    type Item = B;
40
41    fn next(&mut self) -> Option<Self::Item> {
42        match self.inner.next() {
43            Some((_, flag)) => Some(flag),
44            None if !self.done => {
45                self.done = true;
46
47                // After iterating through valid names, if there are any bits left over
48                // then return one final value that includes them. This makes `into_iter`
49                // and `from_iter` roundtrip
50                if !self.inner.remaining().is_empty() {
51                    Some(B::from_bits_retain(self.inner.remaining.bits()))
52                } else {
53                    None
54                }
55            }
56            None => None,
57        }
58    }
59}
60
61/**
62An iterator over flags values.
63
64This iterator only yields flags values for contained, defined, named flags. Any remaining bits
65won't be yielded, but can be found with the [`IterNames::remaining`] method.
66*/
67pub struct IterNames<B: 'static> {
68    flags: &'static [Flag<B>],
69    idx: usize,
70    source: B,
71    remaining: B,
72}
73
74impl<B: Flags> IterNames<B> {
75    pub(crate) fn new(flags: &B) -> Self {
76        IterNames {
77            flags: B::FLAGS,
78            idx: 0,
79            remaining: B::from_bits_retain(flags.bits()),
80            source: B::from_bits_retain(flags.bits()),
81        }
82    }
83}
84
85impl<B: 'static> IterNames<B> {
86    // Used by the bitflags macro
87    #[doc(hidden)]
88    pub const fn __private_const_new(flags: &'static [Flag<B>], source: B, remaining: B) -> Self {
89        IterNames {
90            flags,
91            idx: 0,
92            remaining,
93            source,
94        }
95    }
96
97    /// Get a flags value of any remaining bits that haven't been yielded yet.
98    ///
99    /// Once the iterator has finished, this method can be used to
100    /// check whether or not there are any bits that didn't correspond
101    /// to a contained, defined, named flag remaining.
102    pub fn remaining(&self) -> &B {
103        &self.remaining
104    }
105}
106
107impl<B: Flags> Iterator for IterNames<B> {
108    type Item = (&'static str, B);
109
110    fn next(&mut self) -> Option<Self::Item> {
111        while let Some(flag) = self.flags.get(self.idx) {
112            // Short-circuit if our state is empty
113            if self.remaining.is_empty() {
114                return None;
115            }
116
117            self.idx += 1;
118
119            // Skip unnamed flags
120            if flag.name().is_empty() {
121                continue;
122            }
123
124            let bits = flag.value().bits();
125
126            // If the flag is set in the original source _and_ it has bits that haven't
127            // been covered by a previous flag yet then yield it. These conditions cover
128            // two cases for multi-bit flags:
129            //
130            // 1. When flags partially overlap, such as `0b00000001` and `0b00000101`, we'll
131            // yield both flags.
132            // 2. When flags fully overlap, such as in convenience flags that are a shorthand for others,
133            // we won't yield both flags.
134            if self.source.contains(B::from_bits_retain(bits))
135                && self.remaining.intersects(B::from_bits_retain(bits))
136            {
137                self.remaining.remove(B::from_bits_retain(bits));
138
139                return Some((flag.name(), B::from_bits_retain(bits)));
140            }
141        }
142
143        None
144    }
145}