Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rework the way cfgs are handled #281

Merged
merged 9 commits into from May 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 61 additions & 2 deletions src/bitflags_trait.rs
@@ -1,11 +1,14 @@
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};

#[doc(hidden)]
pub trait ImplementedByBitFlagsMacro {}

/// A trait that is automatically implemented for all bitflags.
///
/// It should not be implemented manually.
pub trait BitFlags: ImplementedByBitFlagsMacro {
type Bits;
type Bits: Bits;

/// Returns an empty set of flags.
fn empty() -> Self;
/// Returns the set containing all flags.
Expand All @@ -15,7 +18,8 @@ pub trait BitFlags: ImplementedByBitFlagsMacro {
/// Convert from underlying bit representation, unless that
/// representation contains bits that do not correspond to a flag.
fn from_bits(bits: Self::Bits) -> Option<Self>
where Self: Sized;
where
Self: Sized;
/// Convert from underlying bit representation, dropping any bits
/// that do not correspond to flags.
fn from_bits_truncate(bits: Self::Bits) -> Self;
Expand Down Expand Up @@ -48,3 +52,58 @@ pub trait BitFlags: ImplementedByBitFlagsMacro {
/// Inserts or removes the specified flags depending on the passed value.
fn set(&mut self, other: Self, value: bool);
}

// Not re-exported
pub trait Sealed {}

/// A private trait that encodes the requirements of underlying bits types that can hold flags.
///
/// This trait may be made public at some future point, but it presents a compatibility hazard
/// so is left internal for now.
#[doc(hidden)]
pub trait Bits:
Clone
+ Copy
+ BitAnd
+ BitAndAssign
+ BitOr
+ BitOrAssign
+ BitXor
+ BitXorAssign
+ Not
+ Sized
+ Sealed
{
/// The value of `Self` where no bits are set.
const EMPTY: Self;

/// The value of `Self` where all bits are set.
const ALL: Self;
}

macro_rules! impl_bits {
($($u:ty, $i:ty,)*) => {
$(
impl Bits for $u {
const EMPTY: $u = 0;
const ALL: $u = <$u>::MAX;
}

impl Bits for $i {
const EMPTY: $i = 0;
const ALL: $i = <$u>::MAX as $i;
}

impl Sealed for $u {}
impl Sealed for $i {}
)*
}
}

impl_bits! {
u8, i8,
u16, i16,
u32, i32,
u64, i64,
u128, i128,
}
124 changes: 54 additions & 70 deletions src/lib.rs
Expand Up @@ -285,8 +285,8 @@ mod bitflags_trait;

#[doc(hidden)]
pub mod __private {
pub use crate::bitflags_trait::{Bits, ImplementedByBitFlagsMacro};
pub use core;
pub use crate::bitflags_trait::ImplementedByBitFlagsMacro;
}

/// The macro used to generate the flag structure.
Expand Down Expand Up @@ -389,45 +389,6 @@ macro_rules! bitflags {
() => {};
}

// A helper macro to implement the `all` function.
#[macro_export(local_inner_macros)]
#[doc(hidden)]
macro_rules! __impl_all_bitflags {
(
$BitFlags:ident: $T:ty {
$(
$(#[$attr:ident $($args:tt)*])*
$Flag:ident = $value:expr;
)+
}
) => {
// See `Debug::fmt` for why this approach is taken.
#[allow(non_snake_case)]
trait __BitFlags {
$(
#[allow(deprecated)]
const $Flag: $T = 0;
)+
}
#[allow(non_snake_case)]
impl __BitFlags for $BitFlags {
$(
__impl_bitflags! {
#[allow(deprecated)]
$(? #[$attr $($args)*])*
const $Flag: $T = Self::$Flag.bits;
}
)+
}
Self { bits: $(<Self as __BitFlags>::$Flag)|+ }
};
(
$BitFlags:ident: $T:ty { }
) => {
Self { bits: 0 }
};
}

#[macro_export(local_inner_macros)]
#[doc(hidden)]
macro_rules! __impl_bitflags {
Expand Down Expand Up @@ -455,7 +416,7 @@ macro_rules! __impl_bitflags {
// Append any extra bits that correspond to flags to the end of the format
let extra_bits = self.bits & !Self::all().bits();

if extra_bits != 0 {
if extra_bits != <$T as $crate::__private::Bits>::EMPTY {
if !first {
f.write_str(" | ")?;
}
Expand Down Expand Up @@ -495,7 +456,14 @@ macro_rules! __impl_bitflags {
}
}

#[allow(dead_code)]
#[allow(
dead_code,
deprecated,
unused_doc_comments,
unused_attributes,
unused_mut,
non_upper_case_globals
)]
impl $BitFlags {
$(
$(#[$attr $($args)*])*
Expand All @@ -505,20 +473,13 @@ macro_rules! __impl_bitflags {
/// Returns an empty set of flags.
#[inline]
pub const fn empty() -> Self {
Self { bits: 0 }
Self { bits: <$T as $crate::__private::Bits>::EMPTY }
}

/// Returns the set containing all flags.
#[inline]
pub const fn all() -> Self {
__impl_all_bitflags! {
$BitFlags: $T {
$(
$(#[$attr $($args)*])*
$Flag = $value;
)*
}
}
Self::from_bits_truncate(<$T as $crate::__private::Bits>::ALL)
}

/// Returns the raw value of the flags currently stored.
Expand All @@ -532,8 +493,9 @@ macro_rules! __impl_bitflags {
#[inline]
pub const fn from_bits(bits: $T) -> $crate::__private::core::option::Option<Self> {
let truncated = Self::from_bits_truncate(bits).bits;

if truncated == bits {
$crate::__private::core::option::Option::Some(Self{ bits })
$crate::__private::core::option::Option::Some(Self { bits })
} else {
$crate::__private::core::option::Option::None
}
Expand All @@ -543,15 +505,13 @@ macro_rules! __impl_bitflags {
/// that do not correspond to flags.
#[inline]
pub const fn from_bits_truncate(bits: $T) -> Self {
if bits == 0 {
if bits == <$T as $crate::__private::Bits>::EMPTY {
return Self { bits }
}

#[allow(unused_mut)]
let mut truncated = 0;
let mut truncated = <$T as $crate::__private::Bits>::EMPTY;

$(
#[allow(unused_doc_comments, unused_attributes)]
$(#[$attr $($args)*])*
if bits & Self::$Flag.bits == Self::$Flag.bits {
truncated |= Self::$Flag.bits
Expand Down Expand Up @@ -719,15 +679,13 @@ macro_rules! __impl_bitflags {
}

/// Returns an iterator over set flags and their names.
pub fn iter(mut self) -> impl $crate::__private::core::iter::Iterator<Item = (&'static str, Self)> {
pub fn iter(self) -> impl $crate::__private::core::iter::Iterator<Item = (&'static str, Self)> {
use $crate::__private::core::iter::Iterator as _;

const NUM_FLAGS: usize = {
#[allow(unused_mut)]
let mut num_flags = 0;

$(
#[allow(unused_doc_comments, unused_attributes)]
$(#[$attr $($args)*])*
{
num_flags += 1;
Expand All @@ -739,13 +697,11 @@ macro_rules! __impl_bitflags {

const OPTIONS: [$BitFlags; NUM_FLAGS] = [
$(
#[allow(unused_doc_comments, unused_attributes)]
$(#[$attr $($args)*])*
$BitFlags::$Flag,
)*
];

#[allow(unused_doc_comments, unused_attributes)]
const OPTIONS_NAMES: [&'static str; NUM_FLAGS] = [
$(
$(#[$attr $($args)*])*
Expand All @@ -754,17 +710,29 @@ macro_rules! __impl_bitflags {
];

let mut start = 0;
let mut state = self;

$crate::__private::core::iter::from_fn(move || {
if self.is_empty() || NUM_FLAGS == 0 {
if state.is_empty() || NUM_FLAGS == 0 {
$crate::__private::core::option::Option::None
} else {
for (flag, flag_name) in OPTIONS[start..NUM_FLAGS].iter().copied()
.zip(OPTIONS_NAMES[start..NUM_FLAGS].iter().copied())
{
start += 1;

// NOTE: We check whether the flag exists in self, but remove it from
// a different value. This ensure that overlapping flags are handled
// properly. Take the following example:
//
// const A: 0b00000001;
// const B: 0b00000101;
//
// Given the bits 0b00000101, both A and B are set. But if we removed A
// as we encountered it we'd be left with 0b00000100, which doesn't
// correspond to a valid flag on its own.
if self.contains(flag) {
self.remove(flag);
state.remove(flag);

return $crate::__private::core::option::Option::Some((flag_name, flag))
}
Expand Down Expand Up @@ -1353,7 +1321,10 @@ mod tests {
assert_eq!(UNION, Flags::A | Flags::C);
assert_eq!(DIFFERENCE, Flags::all() - Flags::A);
assert_eq!(COMPLEMENT, !Flags::C);
assert_eq!(SYM_DIFFERENCE, (Flags::A | Flags::C) ^ (Flags::all() - Flags::A));
assert_eq!(
SYM_DIFFERENCE,
(Flags::A | Flags::C) ^ (Flags::all() - Flags::A)
);
}

#[test]
Expand Down Expand Up @@ -1609,13 +1580,15 @@ mod tests {
assert_eq!(format!("{:?}", Flags::A | Flags::B), "A | B");
assert_eq!(format!("{:?}", Flags::empty()), "(empty)");
assert_eq!(format!("{:?}", Flags::ABC), "A | B | C");

let extra = unsafe { Flags::from_bits_unchecked(0xb8) };

assert_eq!(format!("{:?}", extra), "0xb8");
assert_eq!(format!("{:?}", Flags::A | extra), "A | 0xb8");

assert_eq!(
format!("{:?}", Flags::ABC | extra),
"A | B | C | 0xb8"
"A | B | C | ABC | 0xb8"
);

assert_eq!(format!("{:?}", EmptyFlags::empty()), "(empty)");
Expand Down Expand Up @@ -1830,7 +1803,8 @@ mod tests {
fn test_serde_bitflags_roundtrip() {
let flags = SerdeFlags::A | SerdeFlags::B;

let deserialized: SerdeFlags = serde_json::from_str(&serde_json::to_string(&flags).unwrap()).unwrap();
let deserialized: SerdeFlags =
serde_json::from_str(&serde_json::to_string(&flags).unwrap()).unwrap();

assert_eq!(deserialized.bits, flags.bits);
}
Expand All @@ -1854,7 +1828,6 @@ mod tests {
}
}


let flags = Flags::from_bits(0b00000100);
assert_eq!(flags, None);
let flags = Flags::from_bits(0b00000101);
Expand All @@ -1875,7 +1848,7 @@ mod tests {
let flags = Flags::from_bits_truncate(0b00000101);
assert_eq!(flags, Flags::A);
}

#[test]
fn test_iter() {
bitflags! {
Expand All @@ -1887,24 +1860,31 @@ mod tests {
const FOUR_WIN = 0b1000;
#[cfg(unix)]
const FOUR_UNIX = 0b10000;
const FIVE = 0b01000100;
}
}

let count = {
#[cfg(any(unix, windows))]
{
4
5
}

#[cfg(not(any(unix, windows)))]
{
3
4
}
};

let flags = Flags::all();
assert_eq!(flags.iter().count(), count);

for (_, flag) in flags.iter() {
assert!(flags.contains(flag));
}

let mut iter = flags.iter();

assert_eq!(iter.next().unwrap(), ("ONE", Flags::ONE));
assert_eq!(iter.next().unwrap(), ("TWO", Flags::TWO));
assert_eq!(iter.next().unwrap(), ("THREE", Flags::THREE));
Expand All @@ -1918,14 +1898,18 @@ mod tests {
assert_eq!(iter.next().unwrap(), ("FOUR_WIN", Flags::FOUR_WIN));
}

assert_eq!(iter.next().unwrap(), ("FIVE", Flags::FIVE));

assert_eq!(iter.next(), None);

let flags = Flags::empty();
assert_eq!(flags.iter().count(), 0);

let flags = Flags::ONE | Flags::THREE;
assert_eq!(flags.iter().count(), 2);

let mut iter = flags.iter();

assert_eq!(iter.next().unwrap(), ("ONE", Flags::ONE));
assert_eq!(iter.next().unwrap(), ("THREE", Flags::THREE));
assert_eq!(iter.next(), None);
Expand Down