use std::ops::*;
pub use sawp_flags_derive::BitFlags;
pub trait Primitive:
Default
+ BitOr<Self, Output = Self>
+ BitAnd<Self, Output = Self>
+ BitXor<Self, Output = Self>
+ Not<Output = Self>
+ PartialOrd<Self>
+ std::fmt::Debug
+ std::fmt::Binary
+ Copy
+ Clone
{
}
impl Primitive for u8 {}
impl Primitive for u16 {}
impl Primitive for u32 {}
impl Primitive for u64 {}
impl Primitive for u128 {}
pub trait Flag: Copy + Clone + std::fmt::Debug + std::fmt::Display + 'static {
type Primitive: Primitive;
const ITEMS: &'static [Self];
fn bits(self) -> Self::Primitive;
fn none() -> Flags<Self>;
fn all() -> Flags<Self>;
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct Flags<Enum, Primitive = <Enum as Flag>::Primitive> {
val: Primitive,
marker: std::marker::PhantomData<Enum>,
}
impl<Enum> std::fmt::Debug for Flags<Enum>
where
Enum: Flag,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.val.fmt(f)
}
}
impl<Enum> Default for Flags<Enum>
where
Enum: Flag,
{
fn default() -> Self {
Self {
val: <Enum as Flag>::Primitive::default(),
marker: std::marker::PhantomData,
}
}
}
impl<Enum> Flags<Enum>
where
Enum: Flag,
{
pub fn from_flag(flag: Enum) -> Self {
Self {
val: flag.bits(),
marker: std::marker::PhantomData,
}
}
pub fn from_bits(bits: <Enum as Flag>::Primitive) -> Self {
Self {
val: bits,
marker: std::marker::PhantomData,
}
}
pub fn bits(&self) -> <Enum as Flag>::Primitive {
self.val
}
pub fn bits_ref(&self) -> &<Enum as Flag>::Primitive {
&self.val
}
pub fn intersects<B: Into<Flags<Enum>>>(self, rhs: B) -> bool {
(self & rhs.into()).bits() != Enum::none().bits()
}
pub fn contains<B: Into<Flags<Enum>>>(self, rhs: B) -> bool {
let rhs = rhs.into();
(self & rhs).bits() == rhs.bits()
}
pub fn is_empty(&self) -> bool {
self.bits() == <Enum as Flag>::none().bits()
}
pub fn is_all(&self) -> bool {
self.bits() == <Enum as Flag>::all().bits()
}
}
impl<Enum: Flag> From<Enum> for Flags<Enum> {
fn from(flag: Enum) -> Self {
Self::from_flag(flag)
}
}
impl<Enum: Flag> PartialEq<Enum> for Flags<Enum> {
fn eq(&self, other: &Enum) -> bool {
self.bits() == other.bits()
}
}
impl<T, B> std::ops::BitOr<B> for Flags<T>
where
T: Flag,
B: Into<Flags<T>>,
{
type Output = Flags<T>;
fn bitor(self, other: B) -> Flags<T> {
Flags::from_bits(self.bits() | other.into().bits())
}
}
impl<T, B> std::ops::BitOrAssign<B> for Flags<T>
where
T: Flag,
B: Into<Flags<T>>,
{
fn bitor_assign(&mut self, rhs: B) {
*self = Flags::from_bits(self.bits() | rhs.into().bits())
}
}
impl<T, B> std::ops::BitAnd<B> for Flags<T>
where
T: Flag,
B: Into<Flags<T>>,
{
type Output = Flags<T>;
fn bitand(self, other: B) -> Flags<T> {
Flags::from_bits(self.bits() & other.into().bits())
}
}
impl<T, B> std::ops::BitAndAssign<B> for Flags<T>
where
T: Flag,
B: Into<Flags<T>>,
{
fn bitand_assign(&mut self, rhs: B) {
*self = Flags::from_bits(self.bits() & rhs.into().bits())
}
}
impl<T, B> std::ops::BitXor<B> for Flags<T>
where
T: Flag,
B: Into<Flags<T>>,
{
type Output = Flags<T>;
fn bitxor(self, other: B) -> Flags<T> {
Flags::from_bits(self.bits() ^ other.into().bits())
}
}
impl<T, B> std::ops::BitXorAssign<B> for Flags<T>
where
T: Flag,
B: Into<Flags<T>>,
{
fn bitxor_assign(&mut self, rhs: B) {
*self = Flags::from_bits(self.bits() ^ rhs.into().bits())
}
}
impl<T: Flag> std::ops::Not for Flags<T> {
type Output = Flags<T>;
fn not(self) -> Self::Output {
Flags::from_bits(!self.bits())
}
}
impl<T: Flag> std::fmt::Display for Flags<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let none = self.bits() == T::none().bits();
let mut first = true;
for val in <T as Flag>::ITEMS
.iter()
.cloned()
.filter(move |&flag| self.contains(flag))
{
write!(f, "{}{:?}", if first { "" } else { " | " }, val)?;
first = false;
if none {
return Ok(());
}
}
if none {
write!(f, "NONE")?;
}
Ok(())
}
}
impl<T: Flag> std::fmt::Binary for Flags<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Binary::fmt(&self.bits(), f)
}
}
pub mod example {
use super::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, BitFlags)]
#[repr(u8)]
pub enum Test {
A = 0b0001,
B = 0b0010,
C = 0b0100,
D = 0b1000,
E = Test::A as u8 | Test::B as u8 | Test::C as u8 | Test::D as u8,
}
}
#[cfg(test)]
mod test {
use super::{example::*, *};
#[test]
fn test_enum_bits() {
let bits = 0b1010_1010;
let flags = Flags::<Test>::from_bits(bits);
assert_eq!(bits, flags.bits());
assert_eq!(&bits, flags.bits_ref());
}
#[test]
fn test_enum_or() {
let mut flags = Test::A | Test::B;
assert_eq!(0b0011, flags.bits());
flags |= Test::C;
assert_eq!(0b0111, flags.bits());
flags |= Test::C | Test::D;
assert_eq!(0b1111, flags.bits());
}
#[test]
fn test_enum_and() {
let mut flags = Test::E & Test::B;
assert_eq!(0b0010, flags.bits());
flags &= Test::B;
assert_eq!(0b0010, flags.bits());
flags &= Test::E & Test::B;
assert_eq!(0b0010, flags.bits());
}
#[test]
fn test_enum_xor() {
let mut flags = Test::A ^ Test::B;
assert_eq!(0b0011, flags.bits());
flags ^= Test::C;
assert_eq!(0b0111, flags.bits());
flags ^= Test::D ^ Test::B;
assert_eq!(0b1101, flags.bits());
}
#[test]
fn test_enum_not() {
let flags = !Test::A;
assert_eq!(0b1111_1110, flags.bits());
let flags = !(Test::A ^ Test::B);
assert_eq!(0b1111_1100, flags.bits());
}
#[test]
fn test_contains() {
let flags = Test::A | Test::C;
assert!(flags.contains(Test::A));
assert!(!flags.contains(Test::B));
assert!(!flags.contains(Test::E));
assert!(!flags.contains(Test::B | Test::D));
assert!(!flags.contains(Test::A | Test::B));
assert!(flags.contains(Test::A | Test::C));
}
#[test]
fn test_intersects() {
let flags = Test::A | Test::C;
assert!(flags.intersects(Test::A));
assert!(flags.intersects(Test::E));
assert!(flags.intersects(Test::A | Test::B));
assert!(flags.intersects(Test::A | Test::C));
assert!(flags.intersects(Test::A | Test::B | Test::C));
assert!(!flags.intersects(Test::B | Test::D));
}
#[test]
fn test_eq() {
let flags = Test::A;
assert_eq!(flags, Test::A);
assert_eq!(Test::A, flags);
let flags = Test::A | Test::C;
assert_ne!(flags, Test::A);
assert_ne!(flags, Test::C);
assert_ne!(Test::A, flags);
assert_eq!(flags, Test::A | Test::C);
assert_ne!(flags, Test::A | Test::C | Test::E);
let flags = Flags::<Test>::from_bits(0b1000_0001);
assert_ne!(flags, Test::A);
}
#[test]
fn test_enum_string() {
assert_eq!("NONE", Test::none().to_string());
assert_eq!("A", Test::A.to_string());
assert_eq!("A | B", (Test::A | Test::B).to_string());
assert_eq!("A | B | C | D | E", Test::E.to_string());
assert_eq!("A | B | C | D | E", Flags::from_flag(Test::E).to_string());
}
#[test]
fn test_enum_string_none() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, BitFlags)]
#[repr(u8)]
pub enum Test {
Zero = 0b0000,
A = 0b0001,
B = 0b0010,
C = 0b0100,
D = 0b1000,
E = Test::A as u8 | Test::B as u8 | Test::C as u8 | Test::D as u8,
}
assert_eq!("Zero", Test::Zero.to_string());
assert_eq!("Zero", Test::none().to_string());
assert_eq!("Zero", Flags::from_flag(Test::Zero).to_string());
}
#[test]
fn test_enum_format() {
assert_eq!("A", format!("{:?}", Test::A));
assert_eq!("E", format!("{:?}", Test::E));
assert_eq!("0", format!("{:?}", Test::none()));
assert_eq!("0", format!("{:b}", Test::none()));
assert_eq!("1", format!("{:b}", Test::A));
assert_eq!("1111", format!("{:b}", Test::E));
}
#[test]
fn test_enum_from_str() {
use std::str::FromStr;
assert_eq!(Err(()), Test::from_str(""));
assert_eq!(Ok(Test::A), Test::from_str("a"));
assert_eq!(Ok(Test::A), Test::from_str("A"));
}
#[test]
fn test_all() {
assert_eq!(Test::E, Test::all());
assert!(!Flags::from_flag(Test::A).is_all());
assert!(Flags::from_flag(Test::E).is_all());
}
#[test]
fn test_none() {
assert_eq!(Flags::from_bits(0), Test::none());
assert!(Flags::<Test>::from_bits(0).is_empty());
assert!(!Flags::from_flag(Test::A).is_empty());
assert!(!Flags::from_flag(Test::E).is_empty());
}
}