Skip to main content

bool_flags/
lib.rs

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