1use std::fmt;
2use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
3use minipaste::paste;
4
5macro_rules! create_flags {
6 ($name:ident, $vtype:tt, $n:expr) => {
7 paste! {
8 #[doc = concat!("Condenses ", stringify!($n), " booleans into a single ", stringify!($vtype), ".")]
9 #[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
10 pub struct $name($vtype);
11
12 impl $name {
13 #[doc = "Manually set all flags"]
14 #[cfg_attr(feature = "inline", inline)]
15 pub fn [<from_ $vtype>](value: $vtype) -> $name {
16 $name(value)
17 }
18
19 #[doc = "All flags are false"]
20 #[cfg_attr(feature = "inline", inline)]
21 pub fn none() -> $name {
22 $name($vtype::MIN)
23 }
24
25 #[doc = "All flags are true"]
26 #[cfg_attr(feature = "inline", inline)]
27 pub fn all() -> $name {
28 $name($vtype::MAX)
29 }
30
31 #[doc = "Get the n'th bit (flag)"]
32 #[cfg_attr(feature = "inline", inline)]
33 pub fn get(&self, index: $vtype) -> bool {
34 self.0 & (1 << (index % $n)) != 0
35 }
36
37 #[doc = "Flip the n'th bit (flag)"]
38 #[cfg_attr(feature = "inline", inline)]
39 pub fn flip(&mut self, index: $vtype) {
40 self.0 ^= 1 << (index % $n);
41 }
42
43 #[doc = "Reset the n'th bit (flag) to 0 (false)"]
44 #[cfg_attr(feature = "inline", inline)]
45 pub fn clear(&mut self, index: $vtype) {
46 self.0 &= !(1 << (index % $n));
47 }
48
49 #[doc = "Set the n'th bit (flag) to 1 (true)"]
50 #[cfg_attr(feature = "inline", inline)]
51 pub fn set(&mut self, index: $vtype) {
52 self.0 |= 1 << (index % $n);
53 }
54 }
55
56 impl fmt::Display for $name {
57 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58 write!(f, "0b{:0width$b}", self.0, width = size_of::<$vtype>() * 8)
59 }
60 }
61
62 impl BitAnd for $name {
63 type Output = $name;
64
65 fn bitand(self, rhs: Self) -> Self::Output {
66 Self(self.0 & rhs.0)
67 }
68 }
69
70 impl BitAnd<$vtype> for $name {
71 type Output = $name;
72
73 fn bitand(self, rhs: $vtype) -> Self::Output {
74 Self(self.0 & rhs)
75 }
76 }
77
78 impl BitAndAssign for $name {
79 fn bitand_assign(&mut self, rhs: Self) {
80 self.0 &= rhs.0;
81 }
82 }
83
84 impl BitAndAssign<$vtype> for $name {
85 fn bitand_assign(&mut self, rhs: $vtype) {
86 self.0 &= rhs;
87 }
88 }
89
90 impl BitOr for $name {
91 type Output = $name;
92
93 fn bitor(self, rhs: Self) -> Self::Output {
94 Self(self.0 | rhs.0)
95 }
96 }
97
98 impl BitOr<$vtype> for $name {
99 type Output = $name;
100
101 fn bitor(self, rhs: $vtype) -> Self::Output {
102 Self(self.0 | rhs)
103 }
104 }
105
106 impl BitOrAssign for $name {
107 fn bitor_assign(&mut self, rhs: Self) {
108 self.0 |= rhs.0;
109 }
110 }
111
112 impl BitOrAssign<$vtype> for $name {
113 fn bitor_assign(&mut self, rhs: $vtype) {
114 self.0 |= rhs;
115 }
116 }
117
118 impl BitXor for $name {
119 type Output = $name;
120
121 fn bitxor(self, rhs: Self) -> Self::Output {
122 Self(self.0 ^ rhs.0)
123 }
124 }
125
126 impl BitXor<$vtype> for $name {
127 type Output = $name;
128
129 fn bitxor(self, rhs: $vtype) -> Self::Output {
130 Self(self.0 ^ rhs)
131 }
132 }
133
134 impl BitXorAssign for $name {
135 fn bitxor_assign(&mut self, rhs: Self) {
136 self.0 ^= rhs.0;
137 }
138 }
139
140 impl BitXorAssign<$vtype> for $name {
141 fn bitxor_assign(&mut self, rhs: $vtype) {
142 self.0 ^= rhs;
143 }
144 }
145 }
146 };
147}
148
149create_flags!(Flags8, u8, 8);
150create_flags!(Flags16, u16, 16);
151create_flags!(Flags32, u32, 32);
152create_flags!(Flags64, u64, 64);
153create_flags!(Flags128, u128, 128);
154#[cfg(feature = "usize")]
155create_flags!(FlagsUSize, usize, usize::MAX);
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn functions() {
163 macro_rules! check_functions {
164 ($ftype:tt, $vtype:tt) => {
165 paste! {
167 assert_eq!($ftype::[<from_ $vtype>](1).0, 1);
168 assert_eq!($ftype::[<from_ $vtype>](2).0, 2);
169 assert_eq!($ftype::[<from_ $vtype>](1 | 2).0, 1 | 2);
170 }
171
172 assert_eq!($ftype::none().0, $vtype::MIN);
174 assert_eq!($ftype::all().0, $vtype::MAX);
175
176 let mut flags = $ftype::none();
178 assert_eq!(flags.get(0), false);
179 flags.flip(0);
180 assert_eq!(flags.get(0), true);
181 flags.clear(0);
182 assert_eq!(flags.get(0), false);
183 flags.set(0);
184 assert_eq!(flags.get(0), true);
185
186 println!("{} functions passed", stringify!($ftype));
187 };
188 }
189 check_functions!(Flags8, u8);
190 check_functions!(Flags16, u16);
191 check_functions!(Flags32, u32);
192 check_functions!(Flags64, u64);
193 check_functions!(Flags128, u128);
194 #[cfg(feature = "usize")]
195 check_functions!(FlagsUSize, usize);
196 }
197
198 #[test]
199 fn display() { assert_eq!(format!("{}", Flags8::none()), "0b00000000");
201 assert_eq!(format!("{}", Flags8::all()), "0b11111111");
202 println!("Flags8 display passed");
203
204 assert_eq!(format!("{}", Flags16::none()), "0b0000000000000000");
205 assert_eq!(format!("{}", Flags16::all()), "0b1111111111111111");
206 println!("Flags16 display passed");
207
208 assert_eq!(format!("{}", Flags32::none()), "0b00000000000000000000000000000000");
209 assert_eq!(format!("{}", Flags32::all()), "0b11111111111111111111111111111111");
210 println!("Flags32 display passed");
211
212 assert_eq!(format!("{}", Flags64::none()), "0b0000000000000000000000000000000000000000000000000000000000000000");
213 assert_eq!(format!("{}", Flags64::all()), "0b1111111111111111111111111111111111111111111111111111111111111111");
214 println!("Flags64 display passed");
215
216 assert_eq!(format!("{}", Flags128::none()), "0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
217 assert_eq!(format!("{}", Flags128::all()), "0b11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
218 println!("Flags128 display passed");
219
220 #[cfg(feature = "usize")]
221 {
222 assert_eq!(format!("{}", FlagsUSize::none()), format!("0b{:0width$b}", usize::MIN, width = size_of::<usize>() * 8));
223 assert_eq!(format!("{}", FlagsUSize::all()), format!("0b{:0width$b}", usize::MAX, width = size_of::<usize>() * 8));
224 println!("FlagsUSize display passed");
225 }
226 }
227
228 #[test]
229 fn bit_ops() {
230 macro_rules! check_bit_ops {
231 ($ftype:tt, $vtype:tt) => {
232 let all = $ftype::all();
233 paste! {
234 let one = $ftype::[<from_ $vtype>](1);
235 let not_one = $ftype::[<from_ $vtype>](($vtype::MAX - 1));
236 }
237 assert_eq!(all & one, one);
238 assert_eq!(all & one.0, one);
239
240 assert_eq!(all | one, all);
241 assert_eq!(all | one.0, all);
242
243 assert_eq!(all ^ one, not_one);
244 assert_eq!(all ^ one.0, not_one);
245
246 let mut and_assign = $ftype::all();
248 and_assign &= one;
249 assert_eq!(and_assign, one);
250
251 let mut and_assign = $ftype::all();
252 and_assign &= one.0;
253 assert_eq!(and_assign, one);
254
255 let mut or_assign = $ftype::all();
257 or_assign |= one;
258 assert_eq!(or_assign, all);
259
260 let mut or_assign = $ftype::all();
261 or_assign |= one.0;
262 assert_eq!(or_assign, all);
263
264 let mut xor_assign = $ftype::all();
266 xor_assign ^= one;
267 assert_eq!(xor_assign, not_one);
268
269 let mut xor_assign = $ftype::all();
270 xor_assign ^= one.0;
271 assert_eq!(xor_assign, not_one);
272
273 println!("{} bit ops passed", stringify!($ftype));
274 };
275 }
276 check_bit_ops!(Flags8, u8);
277 check_bit_ops!(Flags16, u16);
278 check_bit_ops!(Flags32, u32);
279 check_bit_ops!(Flags64, u64);
280 check_bit_ops!(Flags128, u128);
281 #[cfg(feature = "usize")]
282 check_bit_ops!(FlagsUSize, usize);
283 }
284}