#[macro_export]
macro_rules! flags {
($flags:ty, $max_flags:expr, $flag_value: ty) => {
$crate::flags!($flags, $max_flags, $flag_value, u8);
};
($flags:ty, $max_flags:expr, $flag_value: ty, $flag_index:ty) => {
$crate::paste::paste! {
#[anchor_lang::zero_copy]
#[derive(PartialEq, Eq)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct [<$flags Container>] {
value: $flag_value,
}
impl Default for [<$flags Container>] {
fn default() -> Self {
type [<$flags Map>] = $crate::bitmaps::Bitmap<$max_flags>;
Self {
value: [<$flags Map>]::new().into_value(),
}
}
}
$crate::impl_flags!($flags, $max_flags, $flag_value, $flag_index);
}
};
}
#[macro_export]
macro_rules! impl_flags {
($flags:ty, $max_flags:expr, $flag_value: ty) => {
$crate::impl_flags!($flags, $max_flags, $flag_value, u8);
};
($flags:ty, $max_flags:expr, $flag_value: ty, $flag_index:ty) => {
$crate::paste::paste! {
type [<$flags Map>] = $crate::bitmaps::Bitmap<$max_flags>;
type [<$flags Value>] = $flag_value;
type [<$flags Index>] = $flag_index;
#[allow(dead_code)]
impl [<$flags Container>] {
fn flag_to_index(flag: $flags) -> usize {
usize::from( [<$flags Index>]::from(flag))
}
fn into_map(self) -> [<$flags Map>] {
[<$flags Map>]::from_value(self.value)
}
pub fn get_flag(&self, flag: $flags) -> bool {
let index = Self::flag_to_index(flag);
let map = self.into_map();
map.get(index)
}
pub fn set_flag(&mut self, flag: $flags, value: bool) -> bool {
let index = Self::flag_to_index(flag);
let mut map = self.into_map();
let previous = map.set(index, value);
self.value = map.into_value();
previous
}
pub fn into_value(self) -> [<$flags Value>] {
self.into_map().into_value()
}
pub fn from_value(value: [<$flags Value>]) -> Self {
Self {
value,
}
}
}
}
};
}
#[cfg(test)]
mod tests {
use bytemuck::Zeroable;
#[derive(num_enum::IntoPrimitive)]
#[repr(u8)]
enum Flags {
Enabled,
}
#[test]
fn basic() {
flags!(Flags, 8, u8);
let mut flags = FlagsContainer::zeroed();
assert!(!flags.get_flag(Flags::Enabled));
let previous = flags.set_flag(Flags::Enabled, true);
assert!(!previous);
assert!(flags.get_flag(Flags::Enabled));
let value = flags.into_value();
assert_eq!(value, 1);
}
}