Skip to main content

bool_flags/
lib.rs

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				// Check from_TYPE
166				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				// Check none and all
173				assert_eq!($ftype::none().0, $vtype::MIN);
174				assert_eq!($ftype::all().0, $vtype::MAX);
175				
176				// Check get, flip, clear, set
177				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() { // Check Display is correct
200		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				// And assign
247				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				// Or assign
256				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				// Xor assign
265				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}