aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/example_generated.rs20
-rw-r--r--src/external.rs92
-rw-r--r--src/external/arbitrary.rs10
-rw-r--r--src/external/bytemuck.rs2
-rw-r--r--src/external/serde.rs30
-rw-r--r--src/internal.rs22
-rw-r--r--src/iter.rs86
-rw-r--r--src/lib.rs2137
-rw-r--r--src/parser.rs91
-rw-r--r--src/public.rs265
-rw-r--r--src/tests.rs131
-rw-r--r--src/tests/all.rs23
-rw-r--r--src/tests/bits.rs36
-rw-r--r--src/tests/complement.rs53
-rw-r--r--src/tests/contains.rs108
-rw-r--r--src/tests/difference.rs92
-rw-r--r--src/tests/empty.rs23
-rw-r--r--src/tests/eq.rs10
-rw-r--r--src/tests/extend.rs42
-rw-r--r--src/tests/flags.rs46
-rw-r--r--src/tests/fmt.rs97
-rw-r--r--src/tests/from_bits.rs45
-rw-r--r--src/tests/from_bits_retain.rs38
-rw-r--r--src/tests/from_bits_truncate.rs42
-rw-r--r--src/tests/from_name.rs42
-rw-r--r--src/tests/insert.rs91
-rw-r--r--src/tests/intersection.rs79
-rw-r--r--src/tests/intersects.rs91
-rw-r--r--src/tests/is_all.rs32
-rw-r--r--src/tests/is_empty.rs31
-rw-r--r--src/tests/iter.rs209
-rw-r--r--src/tests/parser.rs116
-rw-r--r--src/tests/remove.rs100
-rw-r--r--src/tests/symmetric_difference.rs110
-rw-r--r--src/tests/union.rs71
-rw-r--r--src/traits.rs269
36 files changed, 2746 insertions, 2036 deletions
diff --git a/src/example_generated.rs b/src/example_generated.rs
index 7f8a5c5..abb1118 100644
--- a/src/example_generated.rs
+++ b/src/example_generated.rs
@@ -21,16 +21,16 @@ __impl_internal_bitflags! {
// Field `A`.
///
/// This flag has the value `0b00000001`.
- A = 0b00000001;
+ const A = 0b00000001;
/// Field `B`.
///
/// This flag has the value `0b00000010`.
- B = 0b00000010;
+ const B = 0b00000010;
/// Field `C`.
///
/// This flag has the value `0b00000100`.
- C = 0b00000100;
- ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
+ const C = 0b00000100;
+ const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
}
}
@@ -38,6 +38,10 @@ __impl_public_bitflags_forward! {
Flags: u32, Field0
}
+__impl_public_bitflags_ops! {
+ Flags
+}
+
__impl_public_bitflags_iter! {
Flags: u32, Flags
}
@@ -47,15 +51,15 @@ __impl_public_bitflags_consts! {
/// Field `A`.
///
/// This flag has the value `0b00000001`.
- A = 0b00000001;
+ const A = 0b00000001;
/// Field `B`.
///
/// This flag has the value `0b00000010`.
- B = 0b00000010;
+ const B = 0b00000010;
/// Field `C`.
///
/// This flag has the value `0b00000100`.
- C = 0b00000100;
- ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
+ const C = 0b00000100;
+ const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
}
}
diff --git a/src/external.rs b/src/external.rs
index 103b5d1..716af83 100644
--- a/src/external.rs
+++ b/src/external.rs
@@ -14,15 +14,15 @@ Next, re-export the library from the `__private` module here.
Next, define a macro like so:
```rust
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
#[cfg(feature = "serde")]
macro_rules! __impl_external_bitflags_my_library {
(
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
$(
- $(#[$attr:ident $($args:tt)*])*
- $Flag:ident;
+ $(#[$inner:ident $($args:tt)*])*
+ const $Flag:tt;
)*
}
) => {
@@ -30,15 +30,15 @@ macro_rules! __impl_external_bitflags_my_library {
};
}
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
#[cfg(not(feature = "my_library"))]
macro_rules! __impl_external_bitflags_my_library {
(
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
$(
- $(#[$attr:ident $($args:tt)*])*
- $Flag:ident;
+ $(#[$inner:ident $($args:tt)*])*
+ const $Flag:tt;
)*
}
) => {};
@@ -57,8 +57,8 @@ Now, we add our macro call to the `__impl_external_bitflags` macro body:
__impl_external_bitflags_my_library! {
$InternalBitFlags: $T, $PublicBitFlags {
$(
- $(#[$attr $($args)*])*
- $Flag;
+ $(#[$inner $($args)*])*
+ const $Flag;
)*
}
}
@@ -77,14 +77,14 @@ pub(crate) mod __private {
}
/// Implements traits from external libraries for the internal bitflags type.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
macro_rules! __impl_external_bitflags {
(
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
$(
- $(#[$attr:ident $($args:tt)*])*
- $Flag:ident;
+ $(#[$inner:ident $($args:tt)*])*
+ const $Flag:tt;
)*
}
) => {
@@ -92,29 +92,29 @@ macro_rules! __impl_external_bitflags {
// Use `serde` as an example: generate code when the feature is available,
// and a no-op when it isn't
- __impl_external_bitflags_serde! {
+ $crate::__impl_external_bitflags_serde! {
$InternalBitFlags: $T, $PublicBitFlags {
$(
- $(#[$attr $($args)*])*
- $Flag;
+ $(#[$inner $($args)*])*
+ const $Flag;
)*
}
}
- __impl_external_bitflags_arbitrary! {
+ $crate::__impl_external_bitflags_arbitrary! {
$InternalBitFlags: $T, $PublicBitFlags {
$(
- $(#[$attr $($args)*])*
- $Flag;
+ $(#[$inner $($args)*])*
+ const $Flag;
)*
}
}
- __impl_external_bitflags_bytemuck! {
+ $crate::__impl_external_bitflags_bytemuck! {
$InternalBitFlags: $T, $PublicBitFlags {
$(
- $(#[$attr $($args)*])*
- $Flag;
+ $(#[$inner $($args)*])*
+ const $Flag;
)*
}
}
@@ -125,15 +125,15 @@ macro_rules! __impl_external_bitflags {
pub mod serde;
/// Implement `Serialize` and `Deserialize` for the internal bitflags type.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
#[cfg(feature = "serde")]
macro_rules! __impl_external_bitflags_serde {
(
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
$(
- $(#[$attr:ident $($args:tt)*])*
- $Flag:ident;
+ $(#[$inner:ident $($args:tt)*])*
+ const $Flag:tt;
)*
}
) => {
@@ -153,9 +153,7 @@ macro_rules! __impl_external_bitflags_serde {
fn deserialize<D: $crate::__private::serde::Deserializer<'de>>(
deserializer: D,
) -> $crate::__private::core::result::Result<Self, D::Error> {
- let flags: $PublicBitFlags = $crate::serde::deserialize(
- deserializer,
- )?;
+ let flags: $PublicBitFlags = $crate::serde::deserialize(deserializer)?;
Ok(flags.0)
}
@@ -163,15 +161,15 @@ macro_rules! __impl_external_bitflags_serde {
};
}
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
#[cfg(not(feature = "serde"))]
macro_rules! __impl_external_bitflags_serde {
(
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
$(
- $(#[$attr:ident $($args:tt)*])*
- $Flag:ident;
+ $(#[$inner:ident $($args:tt)*])*
+ const $Flag:tt;
)*
}
) => {};
@@ -184,15 +182,15 @@ pub mod arbitrary;
mod bytemuck;
/// Implement `Arbitrary` for the internal bitflags type.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
#[cfg(feature = "arbitrary")]
macro_rules! __impl_external_bitflags_arbitrary {
(
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
$(
- $(#[$attr:ident $($args:tt)*])*
- $Flag:ident;
+ $(#[$inner:ident $($args:tt)*])*
+ const $Flag:tt;
)*
}
) => {
@@ -206,62 +204,58 @@ macro_rules! __impl_external_bitflags_arbitrary {
};
}
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
#[cfg(not(feature = "arbitrary"))]
macro_rules! __impl_external_bitflags_arbitrary {
(
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
$(
- $(#[$attr:ident $($args:tt)*])*
- $Flag:ident;
+ $(#[$inner:ident $($args:tt)*])*
+ const $Flag:tt;
)*
}
) => {};
}
/// Implement `Pod` and `Zeroable` for the internal bitflags type.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
#[cfg(feature = "bytemuck")]
macro_rules! __impl_external_bitflags_bytemuck {
(
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
$(
- $(#[$attr:ident $($args:tt)*])*
- $Flag:ident;
+ $(#[$inner:ident $($args:tt)*])*
+ const $Flag:tt;
)*
}
) => {
// SAFETY: $InternalBitFlags is guaranteed to have the same ABI as $T,
// and $T implements Pod
- unsafe impl $crate::__private::bytemuck::Pod for $InternalBitFlags
- where
- $T: $crate::__private::bytemuck::Pod,
+ unsafe impl $crate::__private::bytemuck::Pod for $InternalBitFlags where
+ $T: $crate::__private::bytemuck::Pod
{
-
}
// SAFETY: $InternalBitFlags is guaranteed to have the same ABI as $T,
// and $T implements Zeroable
- unsafe impl $crate::__private::bytemuck::Zeroable for $InternalBitFlags
- where
- $T: $crate::__private::bytemuck::Zeroable,
+ unsafe impl $crate::__private::bytemuck::Zeroable for $InternalBitFlags where
+ $T: $crate::__private::bytemuck::Zeroable
{
-
}
};
}
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
#[cfg(not(feature = "bytemuck"))]
macro_rules! __impl_external_bitflags_bytemuck {
(
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
$(
- $(#[$attr:ident $($args:tt)*])*
- $Flag:ident;
+ $(#[$inner:ident $($args:tt)*])*
+ const $Flag:tt;
)*
}
) => {};
diff --git a/src/external/arbitrary.rs b/src/external/arbitrary.rs
index ae59677..ea76f0a 100644
--- a/src/external/arbitrary.rs
+++ b/src/external/arbitrary.rs
@@ -2,12 +2,12 @@
use crate::Flags;
-/// Get a random known flags value.
-pub fn arbitrary<'a, B: Flags>(
- u: &mut arbitrary::Unstructured<'a>,
-) -> arbitrary::Result<B>
+/**
+Generate some arbitrary flags value with only known bits set.
+*/
+pub fn arbitrary<'a, B: Flags>(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<B>
where
- B::Bits: arbitrary::Arbitrary<'a>
+ B::Bits: arbitrary::Arbitrary<'a>,
{
B::from_bits(u.arbitrary()?).ok_or_else(|| arbitrary::Error::IncorrectFormat)
}
diff --git a/src/external/bytemuck.rs b/src/external/bytemuck.rs
index 5ab109e..a0cd68c 100644
--- a/src/external/bytemuck.rs
+++ b/src/external/bytemuck.rs
@@ -1,7 +1,7 @@
#[cfg(test)]
mod tests {
use bytemuck::{Pod, Zeroable};
-
+
bitflags! {
#[derive(Pod, Zeroable, Clone, Copy)]
#[repr(transparent)]
diff --git a/src/external/serde.rs b/src/external/serde.rs
index bc1f2ec..be4f2ed 100644
--- a/src/external/serde.rs
+++ b/src/external/serde.rs
@@ -1,17 +1,21 @@
//! Specialized serialization for flags types using `serde`.
+use crate::{
+ parser::{self, ParseHex, WriteHex},
+ Flags,
+};
use core::{fmt, str};
-use crate::{Flags, parser::{self, ParseHex, WriteHex}};
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
-/// Serialize a set of flags as a human-readable string or their underlying bits.
-pub fn serialize<B: Flags, S: Serializer>(
- flags: &B,
- serializer: S,
-) -> Result<S::Ok, S::Error>
+/**
+Serialize a set of flags as a human-readable string or their underlying bits.
+
+Any unknown bits will be retained.
+*/
+pub fn serialize<B: Flags, S: Serializer>(flags: &B, serializer: S) -> Result<S::Ok, S::Error>
where
B::Bits: WriteHex + Serialize,
{
@@ -25,14 +29,12 @@ where
}
}
-/// Deserialize a set of flags from a human-readable string or their underlying bits.
-pub fn deserialize<
- 'de,
- B: Flags,
- D: Deserializer<'de>,
->(
- deserializer: D,
-) -> Result<B, D::Error>
+/**
+Deserialize a set of flags from a human-readable string or their underlying bits.
+
+Any unknown bits will be retained.
+*/
+pub fn deserialize<'de, B: Flags, D: Deserializer<'de>>(deserializer: D) -> Result<B, D::Error>
where
B::Bits: ParseHex + Deserialize<'de>,
{
diff --git a/src/internal.rs b/src/internal.rs
index c4fb653..87d01cc 100644
--- a/src/internal.rs
+++ b/src/internal.rs
@@ -6,7 +6,7 @@
/// Declare the `bitflags`-facing bitflags struct.
///
/// This type is part of the `bitflags` crate's public API, but not part of the user's.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
macro_rules! __declare_internal_bitflags {
(
@@ -25,14 +25,14 @@ macro_rules! __declare_internal_bitflags {
///
/// Methods and trait implementations can be freely added here without breaking end-users.
/// If we want to expose new functionality to `#[derive]`, this is the place to do it.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
macro_rules! __impl_internal_bitflags {
(
$InternalBitFlags:ident: $T:ty, $PublicBitFlags:ident {
$(
- $(#[$attr:ident $($args:tt)*])*
- $Flag:ident = $value:expr;
+ $(#[$inner:ident $($args:tt)*])*
+ const $Flag:tt = $value:expr;
)*
}
) => {
@@ -56,7 +56,7 @@ macro_rules! __impl_internal_bitflags {
if self.is_empty() {
// If no flags are set then write an empty hex flag to avoid
// writing an empty string. In some contexts, like serialization,
- // an empty string is preferrable, but it may be unexpected in
+ // an empty string is preferable, but it may be unexpected in
// others for a format not to produce any output.
//
// We can remove this `0x0` and remain compatible with `FromStr`,
@@ -97,16 +97,20 @@ macro_rules! __impl_internal_bitflags {
// The internal flags type offers a similar API to the public one
- __impl_public_bitflags! {
+ $crate::__impl_public_bitflags! {
$InternalBitFlags: $T, $PublicBitFlags {
$(
- $(#[$attr $($args)*])*
- $Flag;
+ $(#[$inner $($args)*])*
+ const $Flag = $value;
)*
}
}
- __impl_public_bitflags_iter! {
+ $crate::__impl_public_bitflags_ops! {
+ $InternalBitFlags
+ }
+
+ $crate::__impl_public_bitflags_iter! {
$InternalBitFlags: $T, $PublicBitFlags
}
diff --git a/src/iter.rs b/src/iter.rs
index 4b6210e..7f7ce55 100644
--- a/src/iter.rs
+++ b/src/iter.rs
@@ -1,18 +1,21 @@
-//! Iterating over set flag values.
+/*!
+Yield the bits of a source flags value in a set of contained flags values.
+*/
-use crate::{Flags, Flag};
+use crate::{Flag, Flags};
-/// An iterator over a set of flags.
-///
-/// Any bits that don't correspond to a valid flag will be yielded
-/// as a final item from the iterator.
+/**
+An iterator over flags values.
+
+This iterator will yield flags values for contained, defined flags first, with any remaining bits yielded
+as a final flags value.
+*/
pub struct Iter<B: 'static> {
inner: IterNames<B>,
done: bool,
}
impl<B: Flags> Iter<B> {
- /// Create a new iterator over the given set of flags.
pub(crate) fn new(flags: &B) -> Self {
Iter {
inner: IterNames::new(flags),
@@ -22,10 +25,11 @@ impl<B: Flags> Iter<B> {
}
impl<B: 'static> Iter<B> {
+ // Used by the `bitflags` macro
#[doc(hidden)]
- pub const fn __private_const_new(flags: &'static [Flag<B>], source: B, state: B) -> Self {
+ pub const fn __private_const_new(flags: &'static [Flag<B>], source: B, remaining: B) -> Self {
Iter {
- inner: IterNames::__private_const_new(flags, source, state),
+ inner: IterNames::__private_const_new(flags, source, remaining),
done: false,
}
}
@@ -33,18 +37,18 @@ impl<B: 'static> Iter<B> {
impl<B: Flags> Iterator for Iter<B> {
type Item = B;
-
+
fn next(&mut self) -> Option<Self::Item> {
match self.inner.next() {
Some((_, flag)) => Some(flag),
None if !self.done => {
self.done = true;
-
+
// After iterating through valid names, if there are any bits left over
// then return one final value that includes them. This makes `into_iter`
// and `from_iter` roundtrip
if !self.inner.remaining().is_empty() {
- Some(B::from_bits_retain(self.inner.state.bits()))
+ Some(B::from_bits_retain(self.inner.remaining.bits()))
} else {
None
}
@@ -54,80 +58,88 @@ impl<B: Flags> Iterator for Iter<B> {
}
}
-/// An iterator over a set of flags and their names.
-///
-/// Any bits that don't correspond to a valid flag will be ignored.
+/**
+An iterator over flags values.
+
+This iterator only yields flags values for contained, defined, named flags. Any remaining bits
+won't be yielded, but can be found with the [`IterNames::remaining`] method.
+*/
pub struct IterNames<B: 'static> {
flags: &'static [Flag<B>],
idx: usize,
source: B,
- state: B,
+ remaining: B,
}
impl<B: Flags> IterNames<B> {
- /// Create a new iterator over the given set of flags.
pub(crate) fn new(flags: &B) -> Self {
IterNames {
flags: B::FLAGS,
idx: 0,
- state: B::from_bits_retain(flags.bits()),
+ remaining: B::from_bits_retain(flags.bits()),
source: B::from_bits_retain(flags.bits()),
}
}
}
impl<B: 'static> IterNames<B> {
+ // Used by the bitflags macro
#[doc(hidden)]
- pub const fn __private_const_new(flags: &'static [Flag<B>], source: B, state: B) -> Self {
+ pub const fn __private_const_new(flags: &'static [Flag<B>], source: B, remaining: B) -> Self {
IterNames {
flags,
idx: 0,
- state,
+ remaining,
source,
}
}
- /// Get the remaining (unyielded) flags.
+ /// Get a flags value of any remaining bits that haven't been yielded yet.
///
/// Once the iterator has finished, this method can be used to
/// check whether or not there are any bits that didn't correspond
- /// to a valid flag remaining.
+ /// to a contained, defined, named flag remaining.
pub fn remaining(&self) -> &B {
- &self.state
+ &self.remaining
}
}
impl<B: Flags> Iterator for IterNames<B> {
type Item = (&'static str, B);
-
+
fn next(&mut self) -> Option<Self::Item> {
while let Some(flag) = self.flags.get(self.idx) {
// Short-circuit if our state is empty
- if self.state.is_empty() {
+ if self.remaining.is_empty() {
return None;
}
self.idx += 1;
+ // Skip unnamed flags
+ if flag.name().is_empty() {
+ continue;
+ }
+
let bits = flag.value().bits();
- // 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:
+ // If the flag is set in the original source _and_ it has bits that haven't
+ // been covered by a previous flag yet then yield it. These conditions cover
+ // two cases for multi-bit flags:
//
- // 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.source.contains(B::from_bits_retain(bits)) {
- self.state.remove(B::from_bits_retain(bits));
+ // 1. When flags partially overlap, such as `0b00000001` and `0b00000101`, we'll
+ // yield both flags.
+ // 2. When flags fully overlap, such as in convenience flags that are a shorthand for others,
+ // we won't yield both flags.
+ if self.source.contains(B::from_bits_retain(bits))
+ && self.remaining.intersects(B::from_bits_retain(bits))
+ {
+ self.remaining.remove(B::from_bits_retain(bits));
return Some((flag.name(), B::from_bits_retain(bits)));
}
}
-
+
None
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 37d5540..18270f7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -8,425 +8,243 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-//! A typesafe bitmask flag generator useful for sets of C-style flags.
-//! It can be used for creating ergonomic wrappers around C APIs.
-//!
-//! The `bitflags!` macro generates `struct`s that manage a set of flags. The
-//! type of those flags must be some primitive integer.
-//!
-//! # Examples
-//!
-//! ```
-//! use bitflags::bitflags;
-//!
-//! bitflags! {
-//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-//! struct Flags: u32 {
-//! const A = 0b00000001;
-//! const B = 0b00000010;
-//! const C = 0b00000100;
-//! const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
-//! }
-//! }
-//!
-//! fn main() {
-//! let e1 = Flags::A | Flags::C;
-//! let e2 = Flags::B | Flags::C;
-//! assert_eq!((e1 | e2), Flags::ABC); // union
-//! assert_eq!((e1 & e2), Flags::C); // intersection
-//! assert_eq!((e1 - e2), Flags::A); // set difference
-//! assert_eq!(!e2, Flags::A); // set complement
-//! }
-//! ```
-//!
-//! See [`example_generated::Flags`](./example_generated/struct.Flags.html) for documentation of code
-//! generated by the above `bitflags!` expansion.
-//!
-//! # Visibility
-//!
-//! The `bitflags!` macro supports visibility, just like you'd expect when writing a normal
-//! Rust `struct`:
-//!
-//! ```
-//! mod example {
-//! use bitflags::bitflags;
-//!
-//! bitflags! {
-//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-//! pub struct Flags1: u32 {
-//! const A = 0b00000001;
-//! }
-//!
-//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-//! # pub
-//! struct Flags2: u32 {
-//! const B = 0b00000010;
-//! }
-//! }
-//! }
-//!
-//! fn main() {
-//! let flag1 = example::Flags1::A;
-//! let flag2 = example::Flags2::B; // error: const `B` is private
-//! }
-//! ```
-//!
-//! # Attributes
-//!
-//! Attributes can be attached to the generated flags types and their constants as normal.
-//!
-//! # Representation
-//!
-//! It's valid to add a `#[repr(C)]` or `#[repr(transparent)]` attribute to a generated flags type.
-//! The generated flags type is always guaranteed to be a newtype where its only field has the same
-//! ABI as the underlying integer type.
-//!
-//! In this example, `Flags` has the same ABI as `u32`:
-//!
-//! ```
-//! use bitflags::bitflags;
-//!
-//! bitflags! {
-//! #[repr(transparent)]
-//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-//! struct Flags: u32 {
-//! const A = 0b00000001;
-//! const B = 0b00000010;
-//! const C = 0b00000100;
-//! }
-//! }
-//! ```
-//!
-//! # Extending
-//!
-//! Generated flags types belong to you, so you can add trait implementations to them outside
-//! of what the `bitflags!` macro gives:
-//!
-//! ```
-//! use std::fmt;
-//!
-//! use bitflags::bitflags;
-//!
-//! bitflags! {
-//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-//! struct Flags: u32 {
-//! const A = 0b00000001;
-//! const B = 0b00000010;
-//! }
-//! }
-//!
-//! impl Flags {
-//! pub fn clear(&mut self) {
-//! *self.0.bits_mut() = 0;
-//! }
-//! }
-//!
-//! fn main() {
-//! let mut flags = Flags::A | Flags::B;
-//!
-//! flags.clear();
-//! assert!(flags.is_empty());
-//!
-//! assert_eq!(format!("{:?}", Flags::A | Flags::B), "Flags(A | B)");
-//! assert_eq!(format!("{:?}", Flags::B), "Flags(B)");
-//! }
-//! ```
-//!
-//! # What's implemented by `bitflags!`
-//!
-//! The `bitflags!` macro adds some trait implementations and inherent methods
-//! to generated flags types, but leaves room for you to choose the semantics
-//! of others.
-//!
-//! ## Iterators
-//!
-//! The following iterator traits are implemented for generated flags types:
-//!
-//! - `Extend`: adds the union of the instances iterated over.
-//! - `FromIterator`: calculates the union.
-//! - `IntoIterator`: iterates over set flag values.
-//!
-//! ## Formatting
-//!
-//! The following formatting traits are implemented for generated flags types:
-//!
-//! - `Binary`.
-//! - `LowerHex` and `UpperHex`.
-//! - `Octal`.
-//!
-//! Also see the _Debug and Display_ section for details about standard text
-//! representations for flags types.
-//!
-//! ## Operators
-//!
-//! The following operator traits are implemented for the generated `struct`s:
-//!
-//! - `BitOr` and `BitOrAssign`: union
-//! - `BitAnd` and `BitAndAssign`: intersection
-//! - `BitXor` and `BitXorAssign`: toggle
-//! - `Sub` and `SubAssign`: set difference
-//! - `Not`: set complement
-//!
-//! ## Methods
-//!
-//! The following methods are defined for the generated `struct`s:
-//!
-//! - `empty`: an empty set of flags
-//! - `all`: the set of all defined flags
-//! - `bits`: the raw value of the flags currently stored
-//! - `from_bits`: convert from underlying bit representation, unless that
-//! representation contains bits that do not correspond to a
-//! defined flag
-//! - `from_bits_truncate`: convert from underlying bit representation, dropping
-//! any bits that do not correspond to defined flags
-//! - `from_bits_retain`: convert from underlying bit representation, keeping
-//! all bits (even those not corresponding to defined
-//! flags)
-//! - `is_empty`: `true` if no flags are currently stored
-//! - `is_all`: `true` if currently set flags exactly equal all defined flags
-//! - `intersects`: `true` if there are flags common to both `self` and `other`
-//! - `contains`: `true` if all of the flags in `other` are contained within `self`
-//! - `insert`: inserts the specified flags in-place
-//! - `remove`: removes the specified flags in-place
-//! - `toggle`: the specified flags will be inserted if not present, and removed
-//! if they are.
-//! - `set`: inserts or removes the specified flags depending on the passed value
-//! - `intersection`: returns a new set of flags, containing only the flags present
-//! in both `self` and `other` (the argument to the function).
-//! - `union`: returns a new set of flags, containing any flags present in
-//! either `self` or `other` (the argument to the function).
-//! - `difference`: returns a new set of flags, containing all flags present in
-//! `self` without any of the flags present in `other` (the
-//! argument to the function).
-//! - `symmetric_difference`: returns a new set of flags, containing all flags
-//! present in either `self` or `other` (the argument
-//! to the function), but not both.
-//! - `complement`: returns a new set of flags, containing all flags which are
-//! not set in `self`, but which are allowed for this type.
-//!
-//! # What's not implemented by `bitflags!`
-//!
-//! Some functionality is not automatically implemented for generated flags types
-//! by the `bitflags!` macro, even when it reasonably could be. This is so callers
-//! have more freedom to decide on the semantics of their flags types.
-//!
-//! ## `Clone` and `Copy`
-//!
-//! Generated flags types are not automatically copyable, even though they can always
-//! derive both `Clone` and `Copy`.
-//!
-//! ## `Default`
-//!
-//! The `Default` trait is not automatically implemented for the generated structs.
-//!
-//! If your default value is equal to `0` (which is the same value as calling `empty()`
-//! on the generated struct), you can simply derive `Default`:
-//!
-//! ```
-//! use bitflags::bitflags;
-//!
-//! bitflags! {
-//! // Results in default value with bits: 0
-//! #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
-//! struct Flags: u32 {
-//! const A = 0b00000001;
-//! const B = 0b00000010;
-//! const C = 0b00000100;
-//! }
-//! }
-//!
-//! fn main() {
-//! let derived_default: Flags = Default::default();
-//! assert_eq!(derived_default.bits(), 0);
-//! }
-//! ```
-//!
-//! If your default value is not equal to `0` you need to implement `Default` yourself:
-//!
-//! ```
-//! use bitflags::bitflags;
-//!
-//! bitflags! {
-//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-//! struct Flags: u32 {
-//! const A = 0b00000001;
-//! const B = 0b00000010;
-//! const C = 0b00000100;
-//! }
-//! }
-//!
-//! // explicit `Default` implementation
-//! impl Default for Flags {
-//! fn default() -> Flags {
-//! Flags::A | Flags::C
-//! }
-//! }
-//!
-//! fn main() {
-//! let implemented_default: Flags = Default::default();
-//! assert_eq!(implemented_default, (Flags::A | Flags::C));
-//! }
-//! ```
-//!
-//! ## `Debug` and `Display`
-//!
-//! The `Debug` trait can be derived for a reasonable implementation. This library defines a standard
-//! text-based representation for flags that generated flags types can use. For details on the exact
-//! grammar, see the [`parser`] module.
-//!
-//! To support formatting and parsing your generated flags types using that representation, you can implement
-//! the standard `Display` and `FromStr` traits in this fashion:
-//!
-//! ```
-//! use bitflags::bitflags;
-//! use std::{fmt, str};
-//!
-//! bitflags! {
-//! pub struct Flags: u32 {
-//! const A = 1;
-//! const B = 2;
-//! const C = 4;
-//! const D = 8;
-//! }
-//! }
-//!
-//! impl fmt::Debug for Flags {
-//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-//! fmt::Debug::fmt(&self.0, f)
-//! }
-//! }
-//!
-//! impl fmt::Display for Flags {
-//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-//! fmt::Display::fmt(&self.0, f)
-//! }
-//! }
-//!
-//! impl str::FromStr for Flags {
-//! type Err = bitflags::parser::ParseError;
-//!
-//! fn from_str(flags: &str) -> Result<Self, Self::Err> {
-//! Ok(Self(flags.parse()?))
-//! }
-//! }
-//! ```
-//!
-//! ## `PartialEq` and `PartialOrd`
-//!
-//! Equality and ordering can be derived for a reasonable implementation, or implemented manually
-//! for different semantics.
-//!
-//! # Edge cases
-//!
-//! ## Zero Flags
-//!
-//! Flags with a value equal to zero will have some strange behavior that one should be aware of.
-//!
-//! ```
-//! use bitflags::bitflags;
-//!
-//! bitflags! {
-//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-//! struct Flags: u32 {
-//! const NONE = 0b00000000;
-//! const SOME = 0b00000001;
-//! }
-//! }
-//!
-//! fn main() {
-//! let empty = Flags::empty();
-//! let none = Flags::NONE;
-//! let some = Flags::SOME;
-//!
-//! // Zero flags are treated as always present
-//! assert!(empty.contains(Flags::NONE));
-//! assert!(none.contains(Flags::NONE));
-//! assert!(some.contains(Flags::NONE));
-//!
-//! // Zero flags will be ignored when testing for emptiness
-//! assert!(none.is_empty());
-//! }
-//! ```
-//!
-//! Users should generally avoid defining a flag with a value of zero.
-//!
-//! ## Multi-bit Flags
-//!
-//! It is allowed to define a flag with multiple bits set, however such
-//! flags are _not_ treated as a set where any of those bits is a valid
-//! flag. Instead, each flag is treated as a unit when converting from
-//! bits with [`from_bits`] or [`from_bits_truncate`].
-//!
-//! ```
-//! use bitflags::bitflags;
-//!
-//! bitflags! {
-//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-//! struct Flags: u8 {
-//! const F3 = 0b00000011;
-//! }
-//! }
-//!
-//! fn main() {
-//! // This bit pattern does not set all the bits in `F3`, so it is rejected.
-//! assert!(Flags::from_bits(0b00000001).is_none());
-//! assert!(Flags::from_bits_truncate(0b00000001).is_empty());
-//! }
-//! ```
-//!
-//! [`from_bits`]: Flags::from_bits
-//! [`from_bits_truncate`]: Flags::from_bits_truncate
-//!
-//! # The `Flags` trait
-//!
-//! This library defines a `Flags` trait that's implemented by all generated flags types.
-//! The trait makes it possible to work with flags types generically:
-//!
-//! ```
-//! fn count_unset_flags<F: bitflags::Flags>(flags: &F) -> usize {
-//! // Find out how many flags there are in total
-//! let total = F::all().iter().count();
-//!
-//! // Find out how many flags are set
-//! let set = flags.iter().count();
-//!
-//! total - set
-//! }
-//!
-//! use bitflags::bitflags;
-//!
-//! bitflags! {
-//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-//! struct Flags: u32 {
-//! const A = 0b00000001;
-//! const B = 0b00000010;
-//! const C = 0b00000100;
-//! }
-//! }
-//!
-//! assert_eq!(2, count_unset_flags(&Flags::B));
-//! ```
-//!
-//! # The internal field
-//!
-//! This library generates newtypes like:
-//!
-//! ```
-//! # pub struct Field0;
-//! pub struct Flags(Field0);
-//! ```
-//!
-//! You can freely use methods and trait implementations on this internal field as `.0`.
-//! For details on exactly what's generated for it, see the [`Field0`](example_generated/struct.Field0.html)
-//! example docs.
+/*!
+Generate types for C-style flags with ergonomic APIs.
+
+# Getting started
+
+Add `bitflags` to your `Cargo.toml`:
+
+```toml
+[dependencies.bitflags]
+version = "2.4.2"
+```
+
+## Generating flags types
+
+Use the [`bitflags`] macro to generate flags types:
+
+```rust
+use bitflags::bitflags;
+
+bitflags! {
+ pub struct Flags: u32 {
+ const A = 0b00000001;
+ const B = 0b00000010;
+ const C = 0b00000100;
+ }
+}
+```
+
+See the docs for the `bitflags` macro for the full syntax.
+
+Also see the [`example_generated`] module for an example of what the `bitflags` macro generates for a flags type.
+
+### Externally defined flags
+
+If you're generating flags types for an external source, such as a C API, you can define
+an extra unnamed flag as a mask of all bits the external source may ever set. Usually this would be all bits (`!0`):
+
+```rust
+# use bitflags::bitflags;
+bitflags! {
+ pub struct Flags: u32 {
+ const A = 0b00000001;
+ const B = 0b00000010;
+ const C = 0b00000100;
+
+ // The source may set any bits
+ const _ = !0;
+ }
+}
+```
+
+Why should you do this? Generated methods like `all` and truncating operators like `!` only consider
+bits in defined flags. Adding an unnamed flag makes those methods consider additional bits,
+without generating additional constants for them. It helps compatibility when the external source
+may start setting additional bits at any time. The [known and unknown bits](#known-and-unknown-bits)
+section has more details on this behavior.
+
+### Custom derives
+
+You can derive some traits on generated flags types if you enable Cargo features. The following
+libraries are currently supported:
+
+- `serde`: Support `#[derive(Serialize, Deserialize)]`, using text for human-readable formats,
+and a raw number for binary formats.
+- `arbitrary`: Support `#[derive(Arbitrary)]`, only generating flags values with known bits.
+- `bytemuck`: Support `#[derive(Pod, Zeroable)]`, for casting between flags values and their
+underlying bits values.
+
+You can also define your own flags type outside of the [`bitflags`] macro and then use it to generate methods.
+This can be useful if you need a custom `#[derive]` attribute for a library that `bitflags` doesn't
+natively support:
+
+```rust
+# use std::fmt::Debug as SomeTrait;
+# use bitflags::bitflags;
+#[derive(SomeTrait)]
+pub struct Flags(u32);
+
+bitflags! {
+ impl Flags: u32 {
+ const A = 0b00000001;
+ const B = 0b00000010;
+ const C = 0b00000100;
+ }
+}
+```
+
+### Adding custom methods
+
+The [`bitflags`] macro supports attributes on generated flags types within the macro itself, while
+`impl` blocks can be added outside of it:
+
+```rust
+# use bitflags::bitflags;
+bitflags! {
+ // Attributes can be applied to flags types
+ #[repr(transparent)]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+ pub struct Flags: u32 {
+ const A = 0b00000001;
+ const B = 0b00000010;
+ const C = 0b00000100;
+ }
+}
+
+// Impl blocks can be added to flags types
+impl Flags {
+ pub fn as_u64(&self) -> u64 {
+ self.bits() as u64
+ }
+}
+```
+
+## Working with flags values
+
+Use generated constants and standard bitwise operators to interact with flags values:
+
+```rust
+# use bitflags::bitflags;
+# bitflags! {
+# #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+# pub struct Flags: u32 {
+# const A = 0b00000001;
+# const B = 0b00000010;
+# const C = 0b00000100;
+# }
+# }
+// union
+let ab = Flags::A | Flags::B;
+
+// intersection
+let a = ab & Flags::A;
+
+// difference
+let b = ab - Flags::A;
+
+// complement
+let c = !ab;
+```
+
+See the docs for the [`Flags`] trait for more details on operators and how they behave.
+
+# Formatting and parsing
+
+`bitflags` defines a text format that can be used to convert any flags value to and from strings.
+
+See the [`parser`] module for more details.
+
+# Specification
+
+The terminology and behavior of generated flags types is
+[specified in the source repository](https://github.com/bitflags/bitflags/blob/main/spec.md).
+Details are repeated in these docs where appropriate, but is exhaustively listed in the spec. Some
+things are worth calling out explicitly here.
+
+## Flags types, flags values, flags
+
+The spec and these docs use consistent terminology to refer to things in the bitflags domain:
+
+- **Bits type**: A type that defines a fixed number of bits at specific locations.
+- **Flag**: A set of bits in a bits type that may have a unique name.
+- **Flags type**: A set of defined flags over a specific bits type.
+- **Flags value**: An instance of a flags type using its specific bits value for storage.
+
+```
+# use bitflags::bitflags;
+bitflags! {
+ struct FlagsType: u8 {
+// -- Bits type
+// --------- Flags type
+ const A = 1;
+// ----- Flag
+ }
+}
+
+let flag = FlagsType::A;
+// ---- Flags value
+```
+
+## Known and unknown bits
+
+Any bits in a flag you define are called _known bits_. Any other bits are _unknown bits_.
+In the following flags type:
+
+```
+# use bitflags::bitflags;
+bitflags! {
+ struct Flags: u8 {
+ const A = 1;
+ const B = 1 << 1;
+ const C = 1 << 2;
+ }
+}
+```
+
+The known bits are `0b0000_0111` and the unknown bits are `0b1111_1000`.
+
+`bitflags` doesn't guarantee that a flags value will only ever have known bits set, but some operators
+will unset any unknown bits they encounter. In a future version of `bitflags`, all operators will
+unset unknown bits.
+
+If you're using `bitflags` for flags types defined externally, such as from C, you probably want all
+bits to be considered known, in case that external source changes. You can do this using an unnamed
+flag, as described in [externally defined flags](#externally-defined-flags).
+
+## Zero-bit flags
+
+Flags with no bits set should be avoided because they interact strangely with [`Flags::contains`]
+and [`Flags::intersects`]. A zero-bit flag is always contained, but is never intersected. The
+names of zero-bit flags can be parsed, but are never formatted.
+
+## Multi-bit flags
+
+Flags that set multiple bits should be avoided unless each bit is also in a single-bit flag.
+Take the following flags type as an example:
+
+```
+# use bitflags::bitflags;
+bitflags! {
+ struct Flags: u8 {
+ const A = 1;
+ const B = 1 | 1 << 1;
+ }
+}
+```
+
+The result of `Flags::A ^ Flags::B` is `0b0000_0010`, which doesn't correspond to either
+`Flags::A` or `Flags::B` even though it's still a known bit.
+*/
// ANDROID: Use std to allow building as a dylib.
#![cfg_attr(not(any(feature = "std", test, android_dylib)), no_std)]
#![cfg_attr(not(test), forbid(unsafe_code))]
-#![doc(html_root_url = "https://docs.rs/bitflags/2.3.2")]
+#![cfg_attr(test, allow(mixed_script_confusables))]
#[doc(inline)]
-pub use traits::{Flags, Flag, Bits};
+pub use traits::{Bits, Flag, Flags};
pub mod iter;
pub mod parser;
@@ -435,6 +253,7 @@ mod traits;
#[doc(hidden)]
pub mod __private {
+ #[allow(unused_imports)] // Easier than conditionally checking any optional external dependencies
pub use crate::{external::__private::*, traits::__private::*};
pub use core;
@@ -497,71 +316,141 @@ The macros are split into 3 modules:
- `external`: where external library traits are implemented conditionally.
*/
-/// The macro used to generate the flag structure.
-///
-/// See the [crate level docs](../bitflags/index.html) for complete documentation.
-///
-/// # Example
-///
-/// ```
-/// use bitflags::bitflags;
-///
-/// bitflags! {
-/// #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-/// struct Flags: u32 {
-/// const A = 0b00000001;
-/// const B = 0b00000010;
-/// const C = 0b00000100;
-/// const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
-/// }
-/// }
-///
-/// let e1 = Flags::A | Flags::C;
-/// let e2 = Flags::B | Flags::C;
-/// assert_eq!((e1 | e2), Flags::ABC); // union
-/// assert_eq!((e1 & e2), Flags::C); // intersection
-/// assert_eq!((e1 - e2), Flags::A); // set difference
-/// assert_eq!(!e2, Flags::A); // set complement
-/// ```
-///
-/// The generated `struct`s can also be extended with type and trait
-/// implementations:
-///
-/// ```
-/// use std::fmt;
-///
-/// use bitflags::bitflags;
-///
-/// bitflags! {
-/// #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-/// struct Flags: u32 {
-/// const A = 0b00000001;
-/// const B = 0b00000010;
-/// }
-/// }
-///
-/// impl Flags {
-/// pub fn clear(&mut self) {
-/// *self.0.bits_mut() = 0;
-/// }
-/// }
-///
-/// let mut flags = Flags::A | Flags::B;
-///
-/// flags.clear();
-/// assert!(flags.is_empty());
-///
-/// assert_eq!(format!("{:?}", Flags::A | Flags::B), "Flags(A | B)");
-/// assert_eq!(format!("{:?}", Flags::B), "Flags(B)");
-/// ```
-#[macro_export(local_inner_macros)]
+/**
+Generate a flags type.
+
+# `struct` mode
+
+A declaration that begins with `$vis struct` will generate a `struct` for a flags type, along with
+methods and trait implementations for it. The body of the declaration defines flags as constants,
+where each constant is a flags value of the generated flags type.
+
+## Examples
+
+Generate a flags type using `u8` as the bits type:
+
+```
+# use bitflags::bitflags;
+bitflags! {
+ struct Flags: u8 {
+ const A = 1;
+ const B = 1 << 1;
+ const C = 0b0000_0100;
+ }
+}
+```
+
+Flags types are private by default and accept standard visibility modifiers. Flags themselves
+are always public:
+
+```
+# use bitflags::bitflags;
+bitflags! {
+ pub struct Flags: u8 {
+ // Constants are always `pub`
+ const A = 1;
+ }
+}
+```
+
+Flags may refer to other flags using their [`Flags::bits`] value:
+
+```
+# use bitflags::bitflags;
+bitflags! {
+ struct Flags: u8 {
+ const A = 1;
+ const B = 1 << 1;
+ const AB = Flags::A.bits() | Flags::B.bits();
+ }
+}
+```
+
+A single `bitflags` invocation may include zero or more flags type declarations:
+
+```
+# use bitflags::bitflags;
+bitflags! {}
+
+bitflags! {
+ struct Flags1: u8 {
+ const A = 1;
+ }
+
+ struct Flags2: u8 {
+ const A = 1;
+ }
+}
+```
+
+# `impl` mode
+
+A declaration that begins with `impl` will only generate methods and trait implementations for the
+`struct` defined outside of the `bitflags` macro.
+
+The struct itself must be a newtype using the bits type as its field.
+
+The syntax for `impl` mode is identical to `struct` mode besides the starting token.
+
+## Examples
+
+Implement flags methods and traits for a custom flags type using `u8` as its underlying bits type:
+
+```
+# use bitflags::bitflags;
+struct Flags(u8);
+
+bitflags! {
+ impl Flags: u8 {
+ const A = 1;
+ const B = 1 << 1;
+ const C = 0b0000_0100;
+ }
+}
+```
+
+# Named and unnamed flags
+
+Constants in the body of a declaration are flags. The identifier of the constant is the name of
+the flag. If the identifier is `_`, then the flag is unnamed. Unnamed flags don't appear in the
+generated API, but affect how bits are truncated.
+
+## Examples
+
+Adding an unnamed flag that makes all bits known:
+
+```
+# use bitflags::bitflags;
+bitflags! {
+ struct Flags: u8 {
+ const A = 1;
+ const B = 1 << 1;
+
+ const _ = !0;
+ }
+}
+```
+
+Flags types may define multiple unnamed flags:
+
+```
+# use bitflags::bitflags;
+bitflags! {
+ struct Flags: u8 {
+ const _ = 1;
+ const _ = 1 << 1;
+ }
+}
+```
+*/
+#[macro_export]
macro_rules! bitflags {
(
$(#[$outer:meta])*
$vis:vis struct $BitFlags:ident: $T:ty {
$(
$(#[$inner:ident $($args:tt)*])*
- const $Flag:ident = $value:expr;
+ const $Flag:tt = $value:expr;
)*
}
@@ -569,17 +458,17 @@ macro_rules! bitflags {
) => {
// Declared in the scope of the `bitflags!` call
// This type appears in the end-user's API
- __declare_public_bitflags! {
+ $crate::__declare_public_bitflags! {
$(#[$outer])*
$vis struct $BitFlags
}
// Workaround for: https://github.com/bitflags/bitflags/issues/320
- __impl_public_bitflags_consts! {
+ $crate::__impl_public_bitflags_consts! {
$BitFlags: $T {
$(
$(#[$inner $($args)*])*
- $Flag = $value;
+ const $Flag = $value;
)*
}
}
@@ -592,44 +481,51 @@ macro_rules! bitflags {
unused_mut,
unused_imports,
non_upper_case_globals,
- clippy::assign_op_pattern
+ clippy::assign_op_pattern,
+ clippy::indexing_slicing,
+ clippy::same_name_method,
+ clippy::iter_without_into_iter,
)]
const _: () = {
// Declared in a "hidden" scope that can't be reached directly
// These types don't appear in the end-user's API
- __declare_internal_bitflags! {
+ $crate::__declare_internal_bitflags! {
$vis struct InternalBitFlags: $T
}
- __impl_internal_bitflags! {
+ $crate::__impl_internal_bitflags! {
InternalBitFlags: $T, $BitFlags {
$(
$(#[$inner $($args)*])*
- $Flag = $value;
+ const $Flag = $value;
)*
}
}
// This is where new library trait implementations can be added
- __impl_external_bitflags! {
+ $crate::__impl_external_bitflags! {
InternalBitFlags: $T, $BitFlags {
$(
$(#[$inner $($args)*])*
- $Flag;
+ const $Flag;
)*
}
}
- __impl_public_bitflags_forward! {
+ $crate::__impl_public_bitflags_forward! {
$BitFlags: $T, InternalBitFlags
}
- __impl_public_bitflags_iter! {
+ $crate::__impl_public_bitflags_ops! {
+ $BitFlags
+ }
+
+ $crate::__impl_public_bitflags_iter! {
$BitFlags: $T, $BitFlags
}
};
- bitflags! {
+ $crate::bitflags! {
$($t)*
}
};
@@ -637,17 +533,17 @@ macro_rules! bitflags {
impl $BitFlags:ident: $T:ty {
$(
$(#[$inner:ident $($args:tt)*])*
- const $Flag:ident = $value:expr;
+ const $Flag:tt = $value:expr;
)*
}
$($t:tt)*
) => {
- __impl_public_bitflags_consts! {
+ $crate::__impl_public_bitflags_consts! {
$BitFlags: $T {
$(
$(#[$inner $($args)*])*
- $Flag = $value;
+ const $Flag = $value;
)*
}
}
@@ -660,24 +556,29 @@ macro_rules! bitflags {
unused_mut,
unused_imports,
non_upper_case_globals,
- clippy::assign_op_pattern
+ clippy::assign_op_pattern,
+ clippy::iter_without_into_iter,
)]
const _: () = {
- __impl_public_bitflags! {
+ $crate::__impl_public_bitflags! {
$BitFlags: $T, $BitFlags {
$(
$(#[$inner $($args)*])*
- $Flag;
+ const $Flag = $value;
)*
}
}
- __impl_public_bitflags_iter! {
+ $crate::__impl_public_bitflags_ops! {
+ $BitFlags
+ }
+
+ $crate::__impl_public_bitflags_iter! {
$BitFlags: $T, $BitFlags
}
};
- bitflags! {
+ $crate::bitflags! {
$($t)*
}
};
@@ -688,7 +589,7 @@ macro_rules! bitflags {
///
/// We need to be careful about adding new methods and trait implementations here because they
/// could conflict with items added by the end-user.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
macro_rules! __impl_bitflags {
(
@@ -715,80 +616,77 @@ macro_rules! __impl_bitflags {
fn complement($complement0:ident) $complement:block
}
) => {
- #[allow(
- dead_code,
- deprecated,
- unused_attributes
- )]
+ #[allow(dead_code, deprecated, unused_attributes)]
impl $PublicBitFlags {
- /// Returns an empty set of flags.
+ /// Get a flags value with all bits unset.
#[inline]
pub const fn empty() -> Self {
$empty
}
- /// Returns the set containing all flags.
+ /// Get a flags value with all known bits set.
#[inline]
pub const fn all() -> Self {
$all
}
- /// Returns the raw value of the flags currently stored.
+ /// Get the underlying bits value.
+ ///
+ /// The returned value is exactly the bits set in this flags value.
#[inline]
pub const fn bits(&self) -> $T {
let $bits0 = self;
$bits
}
- /// Convert from underlying bit representation, unless that
- /// representation contains bits that do not correspond to a flag.
+ /// Convert from a bits value.
+ ///
+ /// This method will return `None` if any unknown bits are set.
#[inline]
pub const fn from_bits(bits: $T) -> $crate::__private::core::option::Option<Self> {
let $from_bits0 = bits;
$from_bits
}
- /// Convert from underlying bit representation, dropping any bits
- /// that do not correspond to flags.
+ /// Convert from a bits value, unsetting any unknown bits.
#[inline]
pub const fn from_bits_truncate(bits: $T) -> Self {
let $from_bits_truncate0 = bits;
$from_bits_truncate
}
- /// Convert from underlying bit representation, preserving all
- /// bits (even those not corresponding to a defined flag).
+ /// Convert from a bits value exactly.
#[inline]
pub const fn from_bits_retain(bits: $T) -> Self {
let $from_bits_retain0 = bits;
$from_bits_retain
}
- /// Get the value for a flag from its stringified name.
+ /// Get a flags value with the bits of a flag with the given name set.
///
- /// Names are _case-sensitive_, so must correspond exactly to
- /// the identifier given to the flag.
+ /// This method will return `None` if `name` is empty or doesn't
+ /// correspond to any named flag.
#[inline]
pub fn from_name(name: &str) -> $crate::__private::core::option::Option<Self> {
let $from_name0 = name;
$from_name
}
- /// Returns `true` if no flags are currently stored.
+ /// Whether all bits in this flags value are unset.
#[inline]
pub const fn is_empty(&self) -> bool {
let $is_empty0 = self;
$is_empty
}
- /// Returns `true` if all flags are currently set.
+ /// Whether all known bits in this flags value are set.
#[inline]
pub const fn is_all(&self) -> bool {
let $is_all0 = self;
$is_all
}
- /// Returns `true` if there are flags common to both `self` and `other`.
+ /// Whether any set bits in a source flags value are also set in a target flags value.
#[inline]
pub const fn intersects(&self, other: Self) -> bool {
let $intersects0 = self;
@@ -796,7 +694,7 @@ macro_rules! __impl_bitflags {
$intersects
}
- /// Returns `true` if all of the flags in `other` are contained within `self`.
+ /// Whether all set bits in a source flags value are also set in a target flags value.
#[inline]
pub const fn contains(&self, other: Self) -> bool {
let $contains0 = self;
@@ -804,7 +702,7 @@ macro_rules! __impl_bitflags {
$contains
}
- /// Inserts the specified flags in-place.
+ /// The bitwise or (`|`) of the bits in two flags values.
#[inline]
pub fn insert(&mut self, other: Self) {
let $insert0 = self;
@@ -812,7 +710,10 @@ macro_rules! __impl_bitflags {
$insert
}
- /// Removes the specified flags in-place.
+ /// The intersection of a source flags value with the complement of a target flags value (`&!`).
+ ///
+ /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
+ /// `remove` won't truncate `other`, but the `!` operator will.
#[inline]
pub fn remove(&mut self, other: Self) {
let $remove0 = self;
@@ -820,7 +721,7 @@ macro_rules! __impl_bitflags {
$remove
}
- /// Toggles the specified flags in-place.
+ /// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[inline]
pub fn toggle(&mut self, other: Self) {
let $toggle0 = self;
@@ -828,7 +729,7 @@ macro_rules! __impl_bitflags {
$toggle
}
- /// Inserts or removes the specified flags depending on the passed value.
+ /// Call `insert` when `value` is `true` or `remove` when `value` is `false`.
#[inline]
pub fn set(&mut self, other: Self, value: bool) {
let $set0 = self;
@@ -837,16 +738,7 @@ macro_rules! __impl_bitflags {
$set
}
- /// Returns the intersection between the flags in `self` and
- /// `other`.
- ///
- /// Specifically, the returned set contains only the flags which are
- /// present in *both* `self` *and* `other`.
- ///
- /// This is equivalent to using the `&` operator (e.g.
- /// [`ops::BitAnd`]), as in `flags & other`.
- ///
- /// [`ops::BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html
+ /// The bitwise and (`&`) of the bits in two flags values.
#[inline]
#[must_use]
pub const fn intersection(self, other: Self) -> Self {
@@ -855,17 +747,7 @@ macro_rules! __impl_bitflags {
$intersection
}
- /// Returns the union of between the flags in `self` and `other`.
- ///
- /// Specifically, the returned set contains all flags which are
- /// present in *either* `self` *or* `other`, including any which are
- /// present in both (see [`Self::symmetric_difference`] if that
- /// is undesirable).
- ///
- /// This is equivalent to using the `|` operator (e.g.
- /// [`ops::BitOr`]), as in `flags | other`.
- ///
- /// [`ops::BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html
+ /// The bitwise or (`|`) of the bits in two flags values.
#[inline]
#[must_use]
pub const fn union(self, other: Self) -> Self {
@@ -874,18 +756,10 @@ macro_rules! __impl_bitflags {
$union
}
- /// Returns the difference between the flags in `self` and `other`.
- ///
- /// Specifically, the returned set contains all flags present in
- /// `self`, except for the ones present in `other`.
+ /// The intersection of a source flags value with the complement of a target flags value (`&!`).
///
- /// It is also conceptually equivalent to the "bit-clear" operation:
- /// `flags & !other` (and this syntax is also supported).
- ///
- /// This is equivalent to using the `-` operator (e.g.
- /// [`ops::Sub`]), as in `flags - other`.
- ///
- /// [`ops::Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html
+ /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
+ /// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
#[must_use]
pub const fn difference(self, other: Self) -> Self {
@@ -894,19 +768,7 @@ macro_rules! __impl_bitflags {
$difference
}
- /// Returns the [symmetric difference][sym-diff] between the flags
- /// in `self` and `other`.
- ///
- /// Specifically, the returned set contains the flags present which
- /// are present in `self` or `other`, but that are not present in
- /// both. Equivalently, it contains the flags present in *exactly
- /// one* of the sets `self` and `other`.
- ///
- /// This is equivalent to using the `^` operator (e.g.
- /// [`ops::BitXor`]), as in `flags ^ other`.
- ///
- /// [sym-diff]: https://en.wikipedia.org/wiki/Symmetric_difference
- /// [`ops::BitXor`]: https://doc.rust-lang.org/std/ops/trait.BitXor.html
+ /// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[inline]
#[must_use]
pub const fn symmetric_difference(self, other: Self) -> Self {
@@ -915,19 +777,7 @@ macro_rules! __impl_bitflags {
$symmetric_difference
}
- /// Returns the complement of this set of flags.
- ///
- /// Specifically, the returned set contains all the flags which are
- /// not set in `self`, but which are allowed for this type.
- ///
- /// Alternatively, it can be thought of as the set difference
- /// between [`Self::all()`] and `self` (e.g. `Self::all() - self`)
- ///
- /// This is equivalent to using the `!` operator (e.g.
- /// [`ops::Not`]), as in `!flags`.
- ///
- /// [`Self::all()`]: Self::all
- /// [`ops::Not`]: https://doc.rust-lang.org/std/ops/trait.Not.html
+ /// The bitwise negation (`!`) of the bits in a flags value, truncating the result.
#[inline]
#[must_use]
pub const fn complement(self) -> Self {
@@ -948,7 +798,7 @@ macro_rules! __impl_bitflags {
///
/// If you find yourself with an attribute that should be considered expression-safe
/// and isn't, it can be added here.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
macro_rules! __bitflags_expr_safe_attrs {
// Entrypoint: Move all flags and all attributes into `unprocessed` lists
@@ -957,7 +807,7 @@ macro_rules! __bitflags_expr_safe_attrs {
$(#[$inner:ident $($args:tt)*])*
{ $e:expr }
) => {
- __bitflags_expr_safe_attrs! {
+ $crate::__bitflags_expr_safe_attrs! {
expr: { $e },
attrs: {
// All attributes start here
@@ -982,7 +832,7 @@ macro_rules! __bitflags_expr_safe_attrs {
processed: [$($expr:tt)*],
},
) => {
- __bitflags_expr_safe_attrs! {
+ $crate::__bitflags_expr_safe_attrs! {
expr: { $e },
attrs: {
unprocessed: [
@@ -1009,7 +859,7 @@ macro_rules! __bitflags_expr_safe_attrs {
processed: [$($expr:tt)*],
},
) => {
- __bitflags_expr_safe_attrs! {
+ $crate::__bitflags_expr_safe_attrs! {
expr: { $e },
attrs: {
unprocessed: [
@@ -1035,6 +885,30 @@ macro_rules! __bitflags_expr_safe_attrs {
}
}
+/// Implement a flag, which may be a wildcard `_`.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! __bitflags_flag {
+ (
+ {
+ name: _,
+ named: { $($named:tt)* },
+ unnamed: { $($unnamed:tt)* },
+ }
+ ) => {
+ $($unnamed)*
+ };
+ (
+ {
+ name: $Flag:ident,
+ named: { $($named:tt)* },
+ unnamed: { $($unnamed:tt)* },
+ }
+ ) => {
+ $($named)*
+ };
+}
+
#[macro_use]
mod public;
#[macro_use]
@@ -1046,1105 +920,4 @@ mod external;
pub mod example_generated;
#[cfg(test)]
-mod tests {
- use std::{
- collections::hash_map::DefaultHasher,
- fmt,
- hash::{Hash, Hasher},
- str,
- };
-
- #[derive(Debug, PartialEq, Eq)]
- pub struct ManualFlags(u32);
-
- bitflags! {
- #[doc = "> The first principle is that you must not fool yourself — and"]
- #[doc = "> you are the easiest person to fool."]
- #[doc = "> "]
- #[doc = "> - Richard Feynman"]
- #[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
- struct Flags: u32 {
- const A = 0b00000001;
- #[doc = "<pcwalton> macros are way better at generating code than trans is"]
- const B = 0b00000010;
- const C = 0b00000100;
- #[doc = "* cmr bed"]
- #[doc = "* strcat table"]
- #[doc = "<strcat> wait what?"]
- const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
- }
-
- #[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
- struct _CfgFlags: u32 {
- #[cfg(unix)]
- const _CFG_A = 0b01;
- #[cfg(windows)]
- const _CFG_B = 0b01;
- #[cfg(unix)]
- const _CFG_C = Self::_CFG_A.bits() | 0b10;
- }
-
- #[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
- struct AnotherSetOfFlags: i8 {
- const ANOTHER_FLAG = -1_i8;
- }
-
- #[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
- struct LongFlags: u32 {
- const LONG_A = 0b1111111111111111;
- }
-
- impl ManualFlags: u32 {
- const A = 0b00000001;
- #[doc = "<pcwalton> macros are way better at generating code than trans is"]
- const B = 0b00000010;
- const C = 0b00000100;
- #[doc = "* cmr bed"]
- #[doc = "* strcat table"]
- #[doc = "<strcat> wait what?"]
- const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
- }
- }
-
- bitflags! {
- #[derive(Debug, PartialEq, Eq)]
- struct FmtFlags: u16 {
- const 고양이 = 0b0000_0001;
- const 개 = 0b0000_0010;
- const 물고기 = 0b0000_0100;
- const 물고기_고양이 = Self::고양이.bits() | Self::물고기.bits();
- }
- }
-
- impl str::FromStr for FmtFlags {
- type Err = crate::parser::ParseError;
-
- fn from_str(flags: &str) -> Result<Self, Self::Err> {
- Ok(Self(flags.parse()?))
- }
- }
-
- impl fmt::Display for FmtFlags {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.0, f)
- }
- }
-
- bitflags! {
- #[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
- struct EmptyFlags: u32 {
- }
- }
-
- #[test]
- fn test_bits() {
- assert_eq!(Flags::empty().bits(), 0b00000000);
- assert_eq!(Flags::A.bits(), 0b00000001);
- assert_eq!(Flags::ABC.bits(), 0b00000111);
-
- assert_eq!(<Flags as crate::Flags>::bits(&Flags::ABC), 0b00000111);
-
- assert_eq!(AnotherSetOfFlags::empty().bits(), 0b00);
- assert_eq!(AnotherSetOfFlags::ANOTHER_FLAG.bits(), !0_i8);
-
- assert_eq!(EmptyFlags::empty().bits(), 0b00000000);
- }
-
- #[test]
- fn test_from_bits() {
- assert_eq!(Flags::from_bits(0), Some(Flags::empty()));
- assert_eq!(Flags::from_bits(0b1), Some(Flags::A));
- assert_eq!(Flags::from_bits(0b10), Some(Flags::B));
- assert_eq!(Flags::from_bits(0b11), Some(Flags::A | Flags::B));
- assert_eq!(Flags::from_bits(0b1000), None);
-
- assert_eq!(<Flags as crate::Flags>::from_bits(0b11), Some(Flags::A | Flags::B));
-
- assert_eq!(
- AnotherSetOfFlags::from_bits(!0_i8),
- Some(AnotherSetOfFlags::ANOTHER_FLAG)
- );
-
- assert_eq!(EmptyFlags::from_bits(0), Some(EmptyFlags::empty()));
- assert_eq!(EmptyFlags::from_bits(0b1), None);
- }
-
- #[test]
- fn test_from_bits_truncate() {
- assert_eq!(Flags::from_bits_truncate(0), Flags::empty());
- assert_eq!(Flags::from_bits_truncate(0b1), Flags::A);
- assert_eq!(Flags::from_bits_truncate(0b10), Flags::B);
- assert_eq!(Flags::from_bits_truncate(0b11), (Flags::A | Flags::B));
- assert_eq!(Flags::from_bits_truncate(0b1000), Flags::empty());
- assert_eq!(Flags::from_bits_truncate(0b1001), Flags::A);
-
- assert_eq!(<Flags as crate::Flags>::from_bits_truncate(0b11), (Flags::A | Flags::B));
-
- assert_eq!(
- AnotherSetOfFlags::from_bits_truncate(0_i8),
- AnotherSetOfFlags::empty()
- );
-
- assert_eq!(EmptyFlags::from_bits_truncate(0), EmptyFlags::empty());
- assert_eq!(EmptyFlags::from_bits_truncate(0b1), EmptyFlags::empty());
- }
-
- #[test]
- fn test_from_bits_retain() {
- let extra = Flags::from_bits_retain(0b1000);
- assert_eq!(Flags::from_bits_retain(0), Flags::empty());
- assert_eq!(Flags::from_bits_retain(0b1), Flags::A);
- assert_eq!(Flags::from_bits_retain(0b10), Flags::B);
-
- assert_eq!(Flags::from_bits_retain(0b11), (Flags::A | Flags::B));
- assert_eq!(Flags::from_bits_retain(0b1000), (extra | Flags::empty()));
- assert_eq!(Flags::from_bits_retain(0b1001), (extra | Flags::A));
-
- assert_eq!(<Flags as crate::Flags>::from_bits_retain(0b11), (Flags::A | Flags::B));
-
- let extra = EmptyFlags::from_bits_retain(0b1000);
- assert_eq!(
- EmptyFlags::from_bits_retain(0b1000),
- (extra | EmptyFlags::empty())
- );
- }
-
- #[test]
- fn test_is_empty() {
- assert!(Flags::empty().is_empty());
- assert!(!Flags::A.is_empty());
- assert!(!Flags::ABC.is_empty());
-
- assert!(!<Flags as crate::Flags>::is_empty(&Flags::ABC));
-
- assert!(!AnotherSetOfFlags::ANOTHER_FLAG.is_empty());
-
- assert!(EmptyFlags::empty().is_empty());
- assert!(EmptyFlags::all().is_empty());
- }
-
- #[test]
- fn test_is_all() {
- assert!(Flags::all().is_all());
- assert!(!Flags::A.is_all());
- assert!(Flags::ABC.is_all());
-
- let extra = Flags::from_bits_retain(0b1000);
- assert!(!extra.is_all());
- assert!(!(Flags::A | extra).is_all());
- assert!((Flags::ABC | extra).is_all());
-
- assert!(<Flags as crate::Flags>::is_all(&Flags::all()));
-
- assert!(AnotherSetOfFlags::ANOTHER_FLAG.is_all());
-
- assert!(EmptyFlags::all().is_all());
- assert!(EmptyFlags::empty().is_all());
- }
-
- #[test]
- fn test_two_empties_do_not_intersect() {
- let e1 = Flags::empty();
- let e2 = Flags::empty();
- assert!(!e1.intersects(e2));
-
- assert!(!<Flags as crate::Flags>::intersects(&e1, e2));
-
- assert!(AnotherSetOfFlags::ANOTHER_FLAG.intersects(AnotherSetOfFlags::ANOTHER_FLAG));
- }
-
- #[test]
- fn test_empty_does_not_intersect_with_full() {
- let e1 = Flags::empty();
- let e2 = Flags::ABC;
- assert!(!e1.intersects(e2));
-
- assert!(!<Flags as crate::Flags>::intersects(&e1, e2));
- }
-
- #[test]
- fn test_disjoint_intersects() {
- let e1 = Flags::A;
- let e2 = Flags::B;
- assert!(!e1.intersects(e2));
-
- assert!(!<Flags as crate::Flags>::intersects(&e1, e2));
- }
-
- #[test]
- fn test_overlapping_intersects() {
- let e1 = Flags::A;
- let e2 = Flags::A | Flags::B;
- assert!(e1.intersects(e2));
-
- assert!(<Flags as crate::Flags>::intersects(&e1, e2));
- }
-
- #[test]
- fn test_contains() {
- let e1 = Flags::A;
- let e2 = Flags::A | Flags::B;
- assert!(!e1.contains(e2));
- assert!(e2.contains(e1));
- assert!(Flags::ABC.contains(e2));
-
- assert!(<Flags as crate::Flags>::contains(&Flags::ABC, e2));
-
- assert!(AnotherSetOfFlags::ANOTHER_FLAG.contains(AnotherSetOfFlags::ANOTHER_FLAG));
-
- assert!(EmptyFlags::empty().contains(EmptyFlags::empty()));
- }
-
- #[test]
- fn test_insert() {
- let mut e1 = Flags::A;
- let e2 = Flags::A | Flags::B;
- e1.insert(e2);
- assert_eq!(e1, e2);
-
- let mut e1 = Flags::A;
- let e2 = Flags::A | Flags::B;
- <Flags as crate::Flags>::insert(&mut e1, e2);
- assert_eq!(e1, e2);
-
- let mut e3 = AnotherSetOfFlags::empty();
- e3.insert(AnotherSetOfFlags::ANOTHER_FLAG);
- assert_eq!(e3, AnotherSetOfFlags::ANOTHER_FLAG);
- }
-
- #[test]
- fn test_remove() {
- let mut e1 = Flags::A | Flags::B;
- let e2 = Flags::A | Flags::C;
- e1.remove(e2);
- assert_eq!(e1, Flags::B);
-
- let mut e1 = Flags::A | Flags::B;
- let e2 = Flags::A | Flags::C;
- <Flags as crate::Flags>::remove(&mut e1, e2);
- assert_eq!(e1, Flags::B);
-
- let mut e3 = AnotherSetOfFlags::ANOTHER_FLAG;
- e3.remove(AnotherSetOfFlags::ANOTHER_FLAG);
- assert_eq!(e3, AnotherSetOfFlags::empty());
- }
-
- #[test]
- fn test_operators() {
- let e1 = Flags::A | Flags::C;
- let e2 = Flags::B | Flags::C;
- assert_eq!((e1 | e2), Flags::ABC); // union
- assert_eq!((e1 & e2), Flags::C); // intersection
- assert_eq!((e1 - e2), Flags::A); // set difference
- assert_eq!(!e2, Flags::A); // set complement
- assert_eq!(e1 ^ e2, Flags::A | Flags::B); // toggle
- let mut e3 = e1;
- e3.toggle(e2);
- assert_eq!(e3, Flags::A | Flags::B);
-
- let mut m4 = AnotherSetOfFlags::empty();
- m4.toggle(AnotherSetOfFlags::empty());
- assert_eq!(m4, AnotherSetOfFlags::empty());
- }
-
- #[test]
- fn test_operators_unchecked() {
- let extra = Flags::from_bits_retain(0b1000);
- let e1 = Flags::A | Flags::C | extra;
- let e2 = Flags::B | Flags::C;
- assert_eq!((e1 | e2), (Flags::ABC | extra)); // union
- assert_eq!((e1 & e2), Flags::C); // intersection
- assert_eq!((e1 - e2), (Flags::A | extra)); // set difference
- assert_eq!(!e2, Flags::A); // set complement
- assert_eq!(!e1, Flags::B); // set complement
- assert_eq!(e1 ^ e2, Flags::A | Flags::B | extra); // toggle
- let mut e3 = e1;
- e3.toggle(e2);
- assert_eq!(e3, Flags::A | Flags::B | extra);
- }
-
- #[test]
- fn test_set_ops_basic() {
- let ab = Flags::A.union(Flags::B);
- let ac = Flags::A.union(Flags::C);
- let bc = Flags::B.union(Flags::C);
- assert_eq!(ab.bits(), 0b011);
- assert_eq!(bc.bits(), 0b110);
- assert_eq!(ac.bits(), 0b101);
-
- assert_eq!(ab, Flags::B.union(Flags::A));
- assert_eq!(ac, Flags::C.union(Flags::A));
- assert_eq!(bc, Flags::C.union(Flags::B));
-
- assert_eq!(ac, <Flags as crate::Flags>::union(Flags::A, Flags::C));
-
- assert_eq!(ac, Flags::A | Flags::C);
- assert_eq!(bc, Flags::B | Flags::C);
- assert_eq!(ab.union(bc), Flags::ABC);
-
- assert_eq!(ac, Flags::A | Flags::C);
- assert_eq!(bc, Flags::B | Flags::C);
-
- assert_eq!(ac.union(bc), ac | bc);
- assert_eq!(ac.union(bc), Flags::ABC);
- assert_eq!(bc.union(ac), Flags::ABC);
-
- assert_eq!(ac.intersection(bc), ac & bc);
- assert_eq!(ac.intersection(bc), Flags::C);
- assert_eq!(bc.intersection(ac), Flags::C);
-
- assert_eq!(Flags::C, <Flags as crate::Flags>::intersection(ac, bc));
-
- assert_eq!(ac.difference(bc), ac - bc);
- assert_eq!(bc.difference(ac), bc - ac);
- assert_eq!(ac.difference(bc), Flags::A);
- assert_eq!(bc.difference(ac), Flags::B);
-
- assert_eq!(bc, <Flags as crate::Flags>::difference(bc, Flags::A));
-
- assert_eq!(bc.complement(), !bc);
- assert_eq!(bc.complement(), Flags::A);
-
- assert_eq!(Flags::A, <Flags as crate::Flags>::complement(bc));
-
- assert_eq!(ac.symmetric_difference(bc), Flags::A.union(Flags::B));
- assert_eq!(bc.symmetric_difference(ac), Flags::A.union(Flags::B));
-
- assert_eq!(ab, <Flags as crate::Flags>::symmetric_difference(ac, bc));
- }
-
- #[test]
- fn test_set_ops_const() {
- // These just test that these compile and don't cause use-site panics
- // (would be possible if we had some sort of UB)
- const INTERSECT: Flags = Flags::all().intersection(Flags::C);
- const UNION: Flags = Flags::A.union(Flags::C);
- const DIFFERENCE: Flags = Flags::all().difference(Flags::A);
- const COMPLEMENT: Flags = Flags::C.complement();
- const SYM_DIFFERENCE: Flags = UNION.symmetric_difference(DIFFERENCE);
- assert_eq!(INTERSECT, Flags::C);
- 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)
- );
- }
-
- #[test]
- fn test_set_ops_unchecked() {
- let extra = Flags::from_bits_retain(0b1000);
- let e1 = Flags::A.union(Flags::C).union(extra);
- let e2 = Flags::B.union(Flags::C);
- assert_eq!(e1.bits(), 0b1101);
- assert_eq!(e1.union(e2), (Flags::ABC | extra));
- assert_eq!(e1.intersection(e2), Flags::C);
- assert_eq!(e1.difference(e2), Flags::A | extra);
- assert_eq!(e2.difference(e1), Flags::B);
- assert_eq!(e2.complement(), Flags::A);
- assert_eq!(e1.complement(), Flags::B);
- assert_eq!(e1.symmetric_difference(e2), Flags::A | Flags::B | extra); // toggle
- }
-
- #[test]
- fn test_set_ops_exhaustive() {
- // Define a flag that contains gaps to help exercise edge-cases,
- // especially around "unknown" flags (e.g. ones outside of `all()`
- // `from_bits_retain`).
- // - when lhs and rhs both have different sets of unknown flags.
- // - unknown flags at both ends, and in the middle
- // - cases with "gaps".
- bitflags! {
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- struct Test: u16 {
- // Intentionally no `A`
- const B = 0b000000010;
- // Intentionally no `C`
- const D = 0b000001000;
- const E = 0b000010000;
- const F = 0b000100000;
- const G = 0b001000000;
- // Intentionally no `H`
- const I = 0b100000000;
- }
- }
- let iter_test_flags = || (0..=0b111_1111_1111).map(|bits| Test::from_bits_retain(bits));
-
- for a in iter_test_flags() {
- assert_eq!(
- a.complement(),
- Test::from_bits_truncate(!a.bits()),
- "wrong result: !({:?})",
- a,
- );
- assert_eq!(a.complement(), !a, "named != op: !({:?})", a);
- for b in iter_test_flags() {
- // Check that the named operations produce the expected bitwise
- // values.
- assert_eq!(
- a.union(b).bits(),
- a.bits() | b.bits(),
- "wrong result: `{:?}` | `{:?}`",
- a,
- b,
- );
- assert_eq!(
- a.intersection(b).bits(),
- a.bits() & b.bits(),
- "wrong result: `{:?}` & `{:?}`",
- a,
- b,
- );
- assert_eq!(
- a.symmetric_difference(b).bits(),
- a.bits() ^ b.bits(),
- "wrong result: `{:?}` ^ `{:?}`",
- a,
- b,
- );
- assert_eq!(
- a.difference(b).bits(),
- a.bits() & !b.bits(),
- "wrong result: `{:?}` - `{:?}`",
- a,
- b,
- );
- // Note: Difference is checked as both `a - b` and `b - a`
- assert_eq!(
- b.difference(a).bits(),
- b.bits() & !a.bits(),
- "wrong result: `{:?}` - `{:?}`",
- b,
- a,
- );
- // Check that the named set operations are equivalent to the
- // bitwise equivalents
- assert_eq!(a.union(b), a | b, "named != op: `{:?}` | `{:?}`", a, b,);
- assert_eq!(
- a.intersection(b),
- a & b,
- "named != op: `{:?}` & `{:?}`",
- a,
- b,
- );
- assert_eq!(
- a.symmetric_difference(b),
- a ^ b,
- "named != op: `{:?}` ^ `{:?}`",
- a,
- b,
- );
- assert_eq!(a.difference(b), a - b, "named != op: `{:?}` - `{:?}`", a, b,);
- // Note: Difference is checked as both `a - b` and `b - a`
- assert_eq!(b.difference(a), b - a, "named != op: `{:?}` - `{:?}`", b, a,);
- // Verify that the operations which should be symmetric are
- // actually symmetric.
- assert_eq!(a.union(b), b.union(a), "asymmetry: `{:?}` | `{:?}`", a, b,);
- assert_eq!(
- a.intersection(b),
- b.intersection(a),
- "asymmetry: `{:?}` & `{:?}`",
- a,
- b,
- );
- assert_eq!(
- a.symmetric_difference(b),
- b.symmetric_difference(a),
- "asymmetry: `{:?}` ^ `{:?}`",
- a,
- b,
- );
- }
- }
- }
-
- #[test]
- fn test_set() {
- let mut e1 = Flags::A | Flags::C;
- e1.set(Flags::B, true);
- e1.set(Flags::C, false);
-
- assert_eq!(e1, Flags::A | Flags::B);
- }
-
- #[test]
- fn test_assignment_operators() {
- let mut m1 = Flags::empty();
- let e1 = Flags::A | Flags::C;
- // union
- m1 |= Flags::A;
- assert_eq!(m1, Flags::A);
- // intersection
- m1 &= e1;
- assert_eq!(m1, Flags::A);
- // set difference
- m1 -= m1;
- assert_eq!(m1, Flags::empty());
- // toggle
- m1 ^= e1;
- assert_eq!(m1, e1);
- }
-
- #[test]
- fn test_const_fn() {
- const _M1: Flags = Flags::empty();
-
- const M2: Flags = Flags::A;
- assert_eq!(M2, Flags::A);
-
- const M3: Flags = Flags::C;
- assert_eq!(M3, Flags::C);
- }
-
- #[test]
- fn test_extend() {
- let mut flags;
-
- flags = Flags::empty();
- flags.extend([].iter().cloned());
- assert_eq!(flags, Flags::empty());
-
- flags = Flags::empty();
- flags.extend([Flags::A, Flags::B].iter().cloned());
- assert_eq!(flags, Flags::A | Flags::B);
-
- flags = Flags::A;
- flags.extend([Flags::A, Flags::B].iter().cloned());
- assert_eq!(flags, Flags::A | Flags::B);
-
- flags = Flags::B;
- flags.extend([Flags::A, Flags::ABC].iter().cloned());
- assert_eq!(flags, Flags::ABC);
- }
-
- #[test]
- fn test_from_iterator() {
- assert_eq!([].iter().cloned().collect::<Flags>(), Flags::empty());
- assert_eq!(
- [Flags::A, Flags::B].iter().cloned().collect::<Flags>(),
- Flags::A | Flags::B
- );
- assert_eq!(
- [Flags::A, Flags::ABC].iter().cloned().collect::<Flags>(),
- Flags::ABC
- );
- }
-
- #[test]
- fn test_lt() {
- let mut a = Flags::empty();
- let mut b = Flags::empty();
-
- assert!(!(a < b) && !(b < a));
- b = Flags::B;
- assert!(a < b);
- a = Flags::C;
- assert!(!(a < b) && b < a);
- b = Flags::C | Flags::B;
- assert!(a < b);
- }
-
- #[test]
- fn test_ord() {
- let mut a = Flags::empty();
- let mut b = Flags::empty();
-
- assert!(a <= b && a >= b);
- a = Flags::A;
- assert!(a > b && a >= b);
- assert!(b < a && b <= a);
- b = Flags::B;
- assert!(b > a && b >= a);
- assert!(a < b && a <= b);
- }
-
- fn hash<T: Hash>(t: &T) -> u64 {
- let mut s = DefaultHasher::new();
- t.hash(&mut s);
- s.finish()
- }
-
- #[test]
- fn test_hash() {
- let mut x = Flags::empty();
- let mut y = Flags::empty();
- assert_eq!(hash(&x), hash(&y));
- x = Flags::all();
- y = Flags::ABC;
- assert_eq!(hash(&x), hash(&y));
- }
-
- #[test]
- fn test_default() {
- assert_eq!(Flags::empty(), Flags::default());
- }
-
- #[test]
- fn test_debug() {
- assert_eq!(format!("{:?}", Flags::A | Flags::B), "Flags(A | B)");
- assert_eq!(format!("{:?}", Flags::empty()), "Flags(0x0)");
- assert_eq!(format!("{:?}", Flags::ABC), "Flags(A | B | C)");
-
- let extra = Flags::from_bits_retain(0xb8);
-
- assert_eq!(format!("{:?}", extra), "Flags(0xb8)");
- assert_eq!(format!("{:?}", Flags::A | extra), "Flags(A | 0xb8)");
-
- assert_eq!(
- format!("{:?}", Flags::ABC | extra),
- "Flags(A | B | C | ABC | 0xb8)"
- );
-
- assert_eq!(format!("{:?}", EmptyFlags::empty()), "EmptyFlags(0x0)");
- }
-
- #[test]
- fn test_display_from_str_roundtrip() {
- fn format_parse_case<T: fmt::Debug + fmt::Display + str::FromStr + PartialEq>(flags: T) where <T as str::FromStr>::Err: fmt::Display {
- assert_eq!(flags, {
- match flags.to_string().parse::<T>() {
- Ok(flags) => flags,
- Err(e) => panic!("failed to parse `{}`: {}", flags, e),
- }
- });
- }
-
- fn parse_case<T: fmt::Debug + str::FromStr + PartialEq>(expected: T, flags: &str) where <T as str::FromStr>::Err: fmt::Display + fmt::Debug {
- assert_eq!(expected, flags.parse::<T>().unwrap());
- }
-
- bitflags! {
- #[derive(Debug, Eq, PartialEq)]
- pub struct MultiBitFmtFlags: u8 {
- const A = 0b0000_0001u8;
- const B = 0b0001_1110u8;
- }
- }
-
- impl fmt::Display for MultiBitFmtFlags {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.0, f)
- }
- }
-
- impl str::FromStr for MultiBitFmtFlags {
- type Err = crate::parser::ParseError;
-
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- Ok(MultiBitFmtFlags(s.parse()?))
- }
- }
-
- format_parse_case(FmtFlags::empty());
- format_parse_case(FmtFlags::all());
- format_parse_case(FmtFlags::고양이);
- format_parse_case(FmtFlags::고양이 | FmtFlags::개);
- format_parse_case(FmtFlags::물고기_고양이);
- format_parse_case(FmtFlags::from_bits_retain(0xb8));
- format_parse_case(FmtFlags::from_bits_retain(0x20));
- format_parse_case(MultiBitFmtFlags::from_bits_retain(3));
-
- parse_case(FmtFlags::empty(), "");
- parse_case(FmtFlags::empty(), " \r\n\t");
- parse_case(FmtFlags::empty(), "0x0");
-
- parse_case(FmtFlags::고양이, "고양이");
- parse_case(FmtFlags::고양이, " 고양이 ");
- parse_case(FmtFlags::고양이, "고양이 | 고양이 | 고양이");
- parse_case(FmtFlags::고양이, "0x01");
-
- parse_case(FmtFlags::고양이 | FmtFlags::개, "고양이 | 개");
- parse_case(FmtFlags::고양이 | FmtFlags::개, "고양이|개");
- parse_case(FmtFlags::고양이 | FmtFlags::개, "\n고양이|개 ");
-
- parse_case(FmtFlags::고양이 | FmtFlags::물고기, "물고기_고양이");
- }
-
- #[test]
- fn test_from_str_err() {
- fn parse_case(pat: &str, flags: &str) {
- let err = flags.parse::<FmtFlags>().unwrap_err().to_string();
- assert!(err.contains(pat), "`{}` not found in error `{}`", pat, err);
- }
-
- parse_case("empty flag", "|");
- parse_case("empty flag", "|||");
- parse_case("empty flag", "고양이 |");
- parse_case("unrecognized named flag", "NOT_A_FLAG");
- parse_case("unrecognized named flag", "고양이 개");
- parse_case("unrecognized named flag", "고양이 | NOT_A_FLAG");
- parse_case("invalid hex flag", "0xhi");
- parse_case("invalid hex flag", "고양이 | 0xhi");
- }
-
- #[test]
- fn test_binary() {
- assert_eq!(format!("{:b}", Flags::ABC), "111");
- assert_eq!(format!("{:#b}", Flags::ABC), "0b111");
- let extra = Flags::from_bits_retain(0b1010000);
- assert_eq!(format!("{:b}", Flags::ABC | extra), "1010111");
- assert_eq!(format!("{:#b}", Flags::ABC | extra), "0b1010111");
- }
-
- #[test]
- fn test_octal() {
- assert_eq!(format!("{:o}", LongFlags::LONG_A), "177777");
- assert_eq!(format!("{:#o}", LongFlags::LONG_A), "0o177777");
- let extra = LongFlags::from_bits_retain(0o5000000);
- assert_eq!(format!("{:o}", LongFlags::LONG_A | extra), "5177777");
- assert_eq!(format!("{:#o}", LongFlags::LONG_A | extra), "0o5177777");
- }
-
- #[test]
- fn test_lowerhex() {
- assert_eq!(format!("{:x}", LongFlags::LONG_A), "ffff");
- assert_eq!(format!("{:#x}", LongFlags::LONG_A), "0xffff");
- let extra = LongFlags::from_bits_retain(0xe00000);
- assert_eq!(format!("{:x}", LongFlags::LONG_A | extra), "e0ffff");
- assert_eq!(format!("{:#x}", LongFlags::LONG_A | extra), "0xe0ffff");
- }
-
- #[test]
- fn test_upperhex() {
- assert_eq!(format!("{:X}", LongFlags::LONG_A), "FFFF");
- assert_eq!(format!("{:#X}", LongFlags::LONG_A), "0xFFFF");
- let extra = LongFlags::from_bits_retain(0xe00000);
- assert_eq!(format!("{:X}", LongFlags::LONG_A | extra), "E0FFFF");
- assert_eq!(format!("{:#X}", LongFlags::LONG_A | extra), "0xE0FFFF");
- }
-
- mod submodule {
- bitflags! {
- #[derive(Clone, Copy)]
- pub struct PublicFlags: i8 {
- const X = 0;
- }
-
- #[derive(Clone, Copy)]
- struct PrivateFlags: i8 {
- const Y = 0;
- }
- }
-
- #[test]
- fn test_private() {
- let _ = PrivateFlags::Y;
- }
- }
-
- #[test]
- fn test_public() {
- let _ = submodule::PublicFlags::X;
- }
-
- mod t1 {
- mod foo {
- pub type Bar = i32;
- }
-
- bitflags! {
- /// baz
- #[derive(Clone, Copy)]
- struct Flags: foo::Bar {
- const A = 0b00000001;
- #[cfg(foo)]
- const B = 0b00000010;
- #[cfg(foo)]
- const C = 0b00000010;
- }
- }
- }
-
- #[test]
- fn test_in_function() {
- bitflags! {
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- struct Flags: u8 {
- const A = 1;
- #[cfg(any())] // false
- const B = 2;
- }
- }
- assert_eq!(Flags::all(), Flags::A);
- assert_eq!(format!("{:?}", Flags::A), "Flags(A)");
- }
-
- #[test]
- fn test_deprecated() {
- bitflags! {
- #[derive(Clone, Copy)]
- pub struct TestFlags: u32 {
- #[deprecated(note = "Use something else.")]
- const ONE = 1;
- }
- }
- }
-
- #[test]
- fn test_pub_crate() {
- mod module {
- bitflags! {
- #[derive(Clone, Copy)]
- pub (crate) struct Test: u8 {
- const FOO = 1;
- }
- }
- }
-
- assert_eq!(module::Test::FOO.bits(), 1);
- }
-
- #[test]
- fn test_pub_in_module() {
- mod module {
- mod submodule {
- bitflags! {
- // `pub (in super)` means only the module `module` will
- // be able to access this.
- #[derive(Clone, Copy)]
- pub (in super) struct Test: u8 {
- const FOO = 1;
- }
- }
- }
-
- mod test {
- // Note: due to `pub (in super)`,
- // this cannot be accessed directly by the testing code.
- pub(super) fn value() -> u8 {
- super::submodule::Test::FOO.bits()
- }
- }
-
- pub fn value() -> u8 {
- test::value()
- }
- }
-
- assert_eq!(module::value(), 1)
- }
-
- #[test]
- fn test_zero_value_flags() {
- bitflags! {
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- struct Flags: u32 {
- const NONE = 0b0;
- const SOME = 0b1;
- }
- }
-
- assert!(Flags::empty().contains(Flags::NONE));
- assert!(Flags::SOME.contains(Flags::NONE));
- assert!(Flags::NONE.is_empty());
-
- assert_eq!(format!("{:?}", Flags::SOME), "Flags(NONE | SOME)");
- }
-
- #[test]
- fn test_empty_bitflags() {
- bitflags! {}
- }
-
- #[test]
- fn test_u128_bitflags() {
- bitflags! {
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- struct Flags: u128 {
- const A = 0x0000_0000_0000_0000_0000_0000_0000_0001;
- const B = 0x0000_0000_0000_1000_0000_0000_0000_0000;
- const C = 0x8000_0000_0000_0000_0000_0000_0000_0000;
- const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
- }
- }
-
- assert_eq!(Flags::ABC, Flags::A | Flags::B | Flags::C);
- assert_eq!(Flags::A.bits(), 0x0000_0000_0000_0000_0000_0000_0000_0001);
- assert_eq!(Flags::B.bits(), 0x0000_0000_0000_1000_0000_0000_0000_0000);
- assert_eq!(Flags::C.bits(), 0x8000_0000_0000_0000_0000_0000_0000_0000);
- assert_eq!(Flags::ABC.bits(), 0x8000_0000_0000_1000_0000_0000_0000_0001);
- assert_eq!(format!("{:?}", Flags::A), "Flags(A)");
- assert_eq!(format!("{:?}", Flags::B), "Flags(B)");
- assert_eq!(format!("{:?}", Flags::C), "Flags(C)");
- assert_eq!(format!("{:?}", Flags::ABC), "Flags(A | B | C)");
- }
-
- #[test]
- fn test_from_bits_edge_cases() {
- bitflags! {
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- struct Flags: u8 {
- const A = 0b00000001;
- const BC = 0b00000110;
- }
- }
-
- let flags = Flags::from_bits(0b00000100);
- assert_eq!(flags, None);
- let flags = Flags::from_bits(0b00000101);
- assert_eq!(flags, None);
- }
-
- #[test]
- fn test_from_bits_truncate_edge_cases() {
- bitflags! {
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- struct Flags: u8 {
- const A = 0b00000001;
- const BC = 0b00000110;
- }
- }
-
- let flags = Flags::from_bits_truncate(0b00000100);
- assert_eq!(flags, Flags::empty());
- let flags = Flags::from_bits_truncate(0b00000101);
- assert_eq!(flags, Flags::A);
- }
-
- #[test]
- fn test_iter() {
- bitflags! {
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- struct Flags: u32 {
- const ONE = 0b001;
- const TWO = 0b010;
- const THREE = 0b100;
- #[cfg(windows)]
- const FOUR_WIN = 0b1000;
- #[cfg(unix)]
- const FOUR_UNIX = 0b10000;
- const FIVE = 0b01000100;
- }
- }
-
- let count = {
- #[cfg(any(unix, windows))]
- {
- 5
- }
-
- #[cfg(not(any(unix, windows)))]
- {
- 4
- }
- };
-
- let flags = Flags::all();
- assert_eq!(flags.into_iter().count(), count);
-
- for flag in flags.into_iter() {
- assert!(flags.contains(flag));
- }
-
- let mut iter = flags.iter_names();
-
- assert_eq!(iter.next().unwrap(), ("ONE", Flags::ONE));
- assert_eq!(iter.next().unwrap(), ("TWO", Flags::TWO));
- assert_eq!(iter.next().unwrap(), ("THREE", Flags::THREE));
-
- #[cfg(unix)]
- {
- assert_eq!(iter.next().unwrap(), ("FOUR_UNIX", Flags::FOUR_UNIX));
- }
- #[cfg(windows)]
- {
- 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.into_iter().count(), 0);
-
- let flags = Flags::ONE | Flags::THREE;
- assert_eq!(flags.into_iter().count(), 2);
-
- let mut iter = flags.iter_names();
-
- assert_eq!(iter.next().unwrap(), ("ONE", Flags::ONE));
- assert_eq!(iter.next().unwrap(), ("THREE", Flags::THREE));
- assert_eq!(iter.next(), None);
-
- let flags = Flags::from_bits_retain(0b1000_0000);
- assert_eq!(flags.into_iter().count(), 1);
- assert_eq!(flags.iter_names().count(), 0);
- }
-
- #[test]
- fn into_iter_from_iter_roundtrip() {
- let flags = Flags::ABC | Flags::from_bits_retain(0b1000_0000);
-
- assert_eq!(flags, flags.into_iter().collect::<Flags>());
- }
-
- #[test]
- fn test_from_name() {
- let flags = Flags::all();
-
- let mut rebuilt = Flags::empty();
-
- for (name, value) in flags.iter_names() {
- assert_eq!(value, Flags::from_name(name).unwrap());
-
- rebuilt |= Flags::from_name(name).unwrap();
- }
-
- assert_eq!(flags, rebuilt);
- }
-
- #[test]
- fn bits_types() {
- bitflags! {
- pub struct I8: i8 {
- const A = 1;
- }
-
- pub struct I16: i16 {
- const A = 1;
- }
-
- pub struct I32: i32 {
- const A = 1;
- }
-
- pub struct I64: i64 {
- const A = 1;
- }
-
- pub struct I128: i128 {
- const A = 1;
- }
-
- pub struct Isize: isize {
- const A = 1;
- }
-
- pub struct U8: u8 {
- const A = 1;
- }
-
- pub struct U16: u16 {
- const A = 1;
- }
-
- pub struct U32: u32 {
- const A = 1;
- }
-
- pub struct U64: u64 {
- const A = 1;
- }
-
- pub struct U128: u128 {
- const A = 1;
- }
-
- pub struct Usize: usize {
- const A = 1;
- }
- }
- }
-}
+mod tests;
diff --git a/src/parser.rs b/src/parser.rs
index aac9042..130dc2e 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -1,41 +1,44 @@
-//! Parsing flags from text.
-//!
-//! `bitflags` defines the following *whitespace-insensitive*, *case-sensitive* grammar for flags formatted
-//! as text:
-//!
-//! - _Flags:_ (_Flag_)`|`*
-//! - _Flag:_ _Identifier_ | _HexNumber_
-//! - _Identifier:_ Any Rust identifier
-//! - _HexNumber_: `0x`([0-9a-fA-F])*
-//!
-//! As an example, this is how `Flags::A | Flags::B | 0x0c` can be represented as text:
-//!
-//! ```text
-//! A | B | 0x0c
-//! ```
-//!
-//! Alternatively, it could be represented without whitespace:
-//!
-//! ```text
-//! A|B|0x0C
-//! ```
-//!
-//! Note that identifiers are *case-sensitive*, so the following is *not equivalent*:
-//!
-//! ```text
-//! a | b | 0x0c
-//! ```
+/*!
+Parsing flags from text.
+
+Format and parse a flags value as text using the following grammar:
+
+- _Flags:_ (_Whitespace_ _Flag_ _Whitespace_)`|`*
+- _Flag:_ _Name_ | _Hex Number_
+- _Name:_ The name of any defined flag
+- _Hex Number_: `0x`([0-9a-fA-F])*
+- _Whitespace_: (\s)*
+
+As an example, this is how `Flags::A | Flags::B | 0x0c` can be represented as text:
+
+```text
+A | B | 0x0c
+```
+
+Alternatively, it could be represented without whitespace:
+
+```text
+A|B|0x0C
+```
+
+Note that identifiers are *case-sensitive*, so the following is *not equivalent*:
+
+```text
+a|b|0x0C
+```
+*/
#![allow(clippy::let_unit_value)]
use core::fmt::{self, Write};
-use crate::{Flags, Bits};
+use crate::{Bits, Flags};
+
+/**
+Write a flags value as text.
-/// Write a set of flags to a writer.
-///
-/// Any bits that don't correspond to a valid flag will be formatted
-/// as a hex number.
+Any bits that aren't part of a contained flag will be formatted as a hex number.
+*/
pub fn to_writer<B: Flags>(flags: &B, mut writer: impl Write) -> Result<(), fmt::Error>
where
B::Bits: WriteHex,
@@ -48,7 +51,7 @@ where
// followed by a hex number of any remaining bits that are set
// but don't correspond to any flags.
- // Iterate over the valid flags
+ // Iterate over known flag values
let mut first = true;
let mut iter = flags.iter_names();
for (name, _) in &mut iter {
@@ -85,9 +88,12 @@ where
}
}
-/// Parse a set of flags from text.
-///
-/// This function will fail on unknown flags rather than ignore them.
+/**
+Parse a flags value from text.
+
+This function will fail on any names that don't correspond to defined flags.
+Unknown bits will be retained.
+*/
pub fn from_str<B: Flags>(input: &str) -> Result<B, ParseError>
where
B::Bits: ParseHex,
@@ -110,7 +116,8 @@ where
// If the flag starts with `0x` then it's a hex number
// Parse it directly to the underlying bits type
let parsed_flag = if let Some(flag) = flag.strip_prefix("0x") {
- let bits = <B::Bits>::parse_hex(flag).map_err(|_| ParseError::invalid_hex_flag(flag))?;
+ let bits =
+ <B::Bits>::parse_hex(flag).map_err(|_| ParseError::invalid_hex_flag(flag))?;
B::from_bits_retain(bits)
}
@@ -127,13 +134,19 @@ where
Ok(parsed_flags)
}
-/// Encode a value as a hex number.
+/**
+Encode a value as a hex string.
+
+Implementors of this trait should not write the `0x` prefix.
+*/
pub trait WriteHex {
/// Write the value as hex.
fn write_hex<W: fmt::Write>(&self, writer: W) -> fmt::Result;
}
-/// Parse a value from a number encoded as a hex string.
+/**
+Parse a value from a hex string.
+*/
pub trait ParseHex {
/// Parse the value from hex.
fn parse_hex(input: &str) -> Result<Self, ParseError>
diff --git a/src/public.rs b/src/public.rs
index 57ab0ea..dc2d726 100644
--- a/src/public.rs
+++ b/src/public.rs
@@ -6,7 +6,7 @@
/// Declare the user-facing bitflags struct.
///
/// This type is guaranteed to be a newtype with a `bitflags`-facing type as its single field.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
macro_rules! __declare_public_bitflags {
(
@@ -22,13 +22,13 @@ macro_rules! __declare_public_bitflags {
///
/// We need to be careful about adding new methods and trait implementations here because they
/// could conflict with items added by the end-user.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
macro_rules! __impl_public_bitflags_forward {
(
$PublicBitFlags:ident: $T:ty, $InternalBitFlags:ident
) => {
- __impl_bitflags! {
+ $crate::__impl_bitflags! {
$PublicBitFlags: $T {
fn empty() {
Self($InternalBitFlags::empty())
@@ -117,8 +117,6 @@ macro_rules! __impl_public_bitflags_forward {
}
}
}
-
- __impl_public_bitflags_ops!($PublicBitFlags);
};
}
@@ -126,25 +124,41 @@ macro_rules! __impl_public_bitflags_forward {
///
/// We need to be careful about adding new methods and trait implementations here because they
/// could conflict with items added by the end-user.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
macro_rules! __impl_public_bitflags {
(
$BitFlags:ident: $T:ty, $PublicBitFlags:ident {
$(
- $(#[$attr:ident $($args:tt)*])*
- $Flag:ident;
+ $(#[$inner:ident $($args:tt)*])*
+ const $Flag:tt = $value:expr;
)*
}
) => {
- __impl_bitflags! {
+ $crate::__impl_bitflags! {
$BitFlags: $T {
fn empty() {
Self(<$T as $crate::Bits>::EMPTY)
}
fn all() {
- Self::from_bits_truncate(<$T as $crate::Bits>::ALL)
+ let mut truncated = <$T as $crate::Bits>::EMPTY;
+ let mut i = 0;
+
+ $(
+ $crate::__bitflags_expr_safe_attrs!(
+ $(#[$inner $($args)*])*
+ {{
+ let flag = <$PublicBitFlags as $crate::Flags>::FLAGS[i].value().bits();
+
+ truncated = truncated | flag;
+ i += 1;
+ }}
+ );
+ )*
+
+ let _ = i;
+ Self::from_bits_retain(truncated)
}
fn bits(f) {
@@ -162,24 +176,7 @@ macro_rules! __impl_public_bitflags {
}
fn from_bits_truncate(bits) {
- if bits == <$T as $crate::Bits>::EMPTY {
- return Self(bits)
- }
-
- let mut truncated = <$T as $crate::Bits>::EMPTY;
-
- $(
- __bitflags_expr_safe_attrs!(
- $(#[$attr $($args)*])*
- {
- if bits & $PublicBitFlags::$Flag.bits() == $PublicBitFlags::$Flag.bits() {
- truncated = truncated | $PublicBitFlags::$Flag.bits()
- }
- }
- );
- )*
-
- Self(truncated)
+ Self(bits & Self::all().bits())
}
fn from_bits_retain(bits) {
@@ -188,14 +185,20 @@ macro_rules! __impl_public_bitflags {
fn from_name(name) {
$(
- __bitflags_expr_safe_attrs!(
- $(#[$attr $($args)*])*
- {
- if name == $crate::__private::core::stringify!($Flag) {
- return $crate::__private::core::option::Option::Some(Self($PublicBitFlags::$Flag.bits()));
- }
- }
- );
+ $crate::__bitflags_flag!({
+ name: $Flag,
+ named: {
+ $crate::__bitflags_expr_safe_attrs!(
+ $(#[$inner $($args)*])*
+ {
+ if name == $crate::__private::core::stringify!($Flag) {
+ return $crate::__private::core::option::Option::Some(Self($PublicBitFlags::$Flag.bits()));
+ }
+ }
+ );
+ },
+ unnamed: {},
+ });
)*
let _ = name;
@@ -203,31 +206,33 @@ macro_rules! __impl_public_bitflags {
}
fn is_empty(f) {
- f.0 == Self::empty().0
+ f.bits() == <$T as $crate::Bits>::EMPTY
}
fn is_all(f) {
- Self::all().0 | f.0 == f.0
+ // NOTE: We check against `Self::all` here, not `Self::Bits::ALL`
+ // because the set of all flags may not use all bits
+ Self::all().bits() | f.bits() == f.bits()
}
fn intersects(f, other) {
- !(Self(f.0 & other.0)).is_empty()
+ f.bits() & other.bits() != <$T as $crate::Bits>::EMPTY
}
fn contains(f, other) {
- (f.0 & other.0) == other.0
+ f.bits() & other.bits() == other.bits()
}
fn insert(f, other) {
- f.0 = f.0 | other.0;
+ *f = Self::from_bits_retain(f.bits()).union(other);
}
fn remove(f, other) {
- f.0 = f.0 & !other.0;
+ *f = Self::from_bits_retain(f.bits()).difference(other);
}
fn toggle(f, other) {
- f.0 = f.0 ^ other.0;
+ *f = Self::from_bits_retain(f.bits()).symmetric_difference(other);
}
fn set(f, other, value) {
@@ -239,47 +244,59 @@ macro_rules! __impl_public_bitflags {
}
fn intersection(f, other) {
- Self(f.0 & other.0)
+ Self::from_bits_retain(f.bits() & other.bits())
}
fn union(f, other) {
- Self(f.0 | other.0)
+ Self::from_bits_retain(f.bits() | other.bits())
}
fn difference(f, other) {
- Self(f.0 & !other.0)
+ Self::from_bits_retain(f.bits() & !other.bits())
}
fn symmetric_difference(f, other) {
- Self(f.0 ^ other.0)
+ Self::from_bits_retain(f.bits() ^ other.bits())
}
fn complement(f) {
- Self::from_bits_truncate(!f.0)
+ Self::from_bits_truncate(!f.bits())
}
}
}
-
- __impl_public_bitflags_ops!($BitFlags);
};
}
/// Implement iterators on the public (user-facing) bitflags type.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
macro_rules! __impl_public_bitflags_iter {
($BitFlags:ident: $T:ty, $PublicBitFlags:ident) => {
impl $BitFlags {
- /// Iterate over enabled flag values.
+ /// Yield a set of contained flags values.
+ ///
+ /// Each yielded flags value will correspond to a defined named flag. Any unknown bits
+ /// will be yielded together as a final flags value.
#[inline]
pub const fn iter(&self) -> $crate::iter::Iter<$PublicBitFlags> {
- $crate::iter::Iter::__private_const_new(<$PublicBitFlags as $crate::Flags>::FLAGS, $PublicBitFlags::from_bits_retain(self.bits()), $PublicBitFlags::from_bits_retain(self.bits()))
+ $crate::iter::Iter::__private_const_new(
+ <$PublicBitFlags as $crate::Flags>::FLAGS,
+ $PublicBitFlags::from_bits_retain(self.bits()),
+ $PublicBitFlags::from_bits_retain(self.bits()),
+ )
}
- /// Iterate over enabled flag values with their stringified names.
+ /// Yield a set of contained named flags values.
+ ///
+ /// This method is like [`iter`](#method.iter), except only yields bits in contained named flags.
+ /// Any unknown bits, or bits not corresponding to a contained flag will not be yielded.
#[inline]
pub const fn iter_names(&self) -> $crate::iter::IterNames<$PublicBitFlags> {
- $crate::iter::IterNames::__private_const_new(<$PublicBitFlags as $crate::Flags>::FLAGS, $PublicBitFlags::from_bits_retain(self.bits()), $PublicBitFlags::from_bits_retain(self.bits()))
+ $crate::iter::IterNames::__private_const_new(
+ <$PublicBitFlags as $crate::Flags>::FLAGS,
+ $PublicBitFlags::from_bits_retain(self.bits()),
+ $PublicBitFlags::from_bits_retain(self.bits()),
+ )
}
}
@@ -295,38 +312,54 @@ macro_rules! __impl_public_bitflags_iter {
}
/// Implement traits on the public (user-facing) bitflags type.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
macro_rules! __impl_public_bitflags_ops {
($PublicBitFlags:ident) => {
impl $crate::__private::core::fmt::Binary for $PublicBitFlags {
- fn fmt(&self, f: &mut $crate::__private::core::fmt::Formatter) -> $crate::__private::core::fmt::Result {
- $crate::__private::core::fmt::Binary::fmt(&self.0, f)
+ fn fmt(
+ &self,
+ f: &mut $crate::__private::core::fmt::Formatter,
+ ) -> $crate::__private::core::fmt::Result {
+ let inner = self.0;
+ $crate::__private::core::fmt::Binary::fmt(&inner, f)
}
}
impl $crate::__private::core::fmt::Octal for $PublicBitFlags {
- fn fmt(&self, f: &mut $crate::__private::core::fmt::Formatter) -> $crate::__private::core::fmt::Result {
- $crate::__private::core::fmt::Octal::fmt(&self.0, f)
+ fn fmt(
+ &self,
+ f: &mut $crate::__private::core::fmt::Formatter,
+ ) -> $crate::__private::core::fmt::Result {
+ let inner = self.0;
+ $crate::__private::core::fmt::Octal::fmt(&inner, f)
}
}
impl $crate::__private::core::fmt::LowerHex for $PublicBitFlags {
- fn fmt(&self, f: &mut $crate::__private::core::fmt::Formatter) -> $crate::__private::core::fmt::Result {
- $crate::__private::core::fmt::LowerHex::fmt(&self.0, f)
+ fn fmt(
+ &self,
+ f: &mut $crate::__private::core::fmt::Formatter,
+ ) -> $crate::__private::core::fmt::Result {
+ let inner = self.0;
+ $crate::__private::core::fmt::LowerHex::fmt(&inner, f)
}
}
impl $crate::__private::core::fmt::UpperHex for $PublicBitFlags {
- fn fmt(&self, f: &mut $crate::__private::core::fmt::Formatter) -> $crate::__private::core::fmt::Result {
- $crate::__private::core::fmt::UpperHex::fmt(&self.0, f)
+ fn fmt(
+ &self,
+ f: &mut $crate::__private::core::fmt::Formatter,
+ ) -> $crate::__private::core::fmt::Result {
+ let inner = self.0;
+ $crate::__private::core::fmt::UpperHex::fmt(&inner, f)
}
}
impl $crate::__private::core::ops::BitOr for $PublicBitFlags {
type Output = Self;
- /// Returns the union of the two sets of flags.
+ /// The bitwise or (`|`) of the bits in two flags values.
#[inline]
fn bitor(self, other: $PublicBitFlags) -> Self {
self.union(other)
@@ -334,17 +367,17 @@ macro_rules! __impl_public_bitflags_ops {
}
impl $crate::__private::core::ops::BitOrAssign for $PublicBitFlags {
- /// Adds the set of flags.
+ /// The bitwise or (`|`) of the bits in two flags values.
#[inline]
fn bitor_assign(&mut self, other: Self) {
- self.0 = self.0 | other.0;
+ self.insert(other);
}
}
impl $crate::__private::core::ops::BitXor for $PublicBitFlags {
type Output = Self;
- /// Returns the left flags, but with all the right flags toggled.
+ /// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[inline]
fn bitxor(self, other: Self) -> Self {
self.symmetric_difference(other)
@@ -352,17 +385,17 @@ macro_rules! __impl_public_bitflags_ops {
}
impl $crate::__private::core::ops::BitXorAssign for $PublicBitFlags {
- /// Toggles the set of flags.
+ /// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[inline]
fn bitxor_assign(&mut self, other: Self) {
- self.0 = self.0 ^ other.0
+ self.toggle(other);
}
}
impl $crate::__private::core::ops::BitAnd for $PublicBitFlags {
type Output = Self;
- /// Returns the intersection between the two sets of flags.
+ /// The bitwise and (`&`) of the bits in two flags values.
#[inline]
fn bitand(self, other: Self) -> Self {
self.intersection(other)
@@ -370,17 +403,20 @@ macro_rules! __impl_public_bitflags_ops {
}
impl $crate::__private::core::ops::BitAndAssign for $PublicBitFlags {
- /// Disables all flags disabled in the set.
+ /// The bitwise and (`&`) of the bits in two flags values.
#[inline]
fn bitand_assign(&mut self, other: Self) {
- self.0 = self.0 & other.0;
+ *self = Self::from_bits_retain(self.bits()).intersection(other);
}
}
impl $crate::__private::core::ops::Sub for $PublicBitFlags {
type Output = Self;
- /// Returns the set difference of the two sets of flags.
+ /// The intersection of a source flags value with the complement of a target flags value (`&!`).
+ ///
+ /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
+ /// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
fn sub(self, other: Self) -> Self {
self.difference(other)
@@ -388,17 +424,20 @@ macro_rules! __impl_public_bitflags_ops {
}
impl $crate::__private::core::ops::SubAssign for $PublicBitFlags {
- /// Disables all flags enabled in the set.
+ /// The intersection of a source flags value with the complement of a target flags value (`&!`).
+ ///
+ /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
+ /// `difference` won't truncate `other`, but the `!` operator will.
#[inline]
fn sub_assign(&mut self, other: Self) {
- self.0 = self.0 & !other.0;
+ self.remove(other);
}
}
impl $crate::__private::core::ops::Not for $PublicBitFlags {
type Output = Self;
- /// Returns the complement of this set of flags.
+ /// The bitwise negation (`!`) of the bits in a flags value, truncating the result.
#[inline]
fn not(self) -> Self {
self.complement()
@@ -406,7 +445,11 @@ macro_rules! __impl_public_bitflags_ops {
}
impl $crate::__private::core::iter::Extend<$PublicBitFlags> for $PublicBitFlags {
- fn extend<T: $crate::__private::core::iter::IntoIterator<Item=Self>>(&mut self, iterator: T) {
+ /// The bitwise or (`|`) of the bits in each flags value.
+ fn extend<T: $crate::__private::core::iter::IntoIterator<Item = Self>>(
+ &mut self,
+ iterator: T,
+ ) {
for item in iterator {
self.insert(item)
}
@@ -414,7 +457,10 @@ macro_rules! __impl_public_bitflags_ops {
}
impl $crate::__private::core::iter::FromIterator<$PublicBitFlags> for $PublicBitFlags {
- fn from_iter<T: $crate::__private::core::iter::IntoIterator<Item=Self>>(iterator: T) -> Self {
+ /// The bitwise or (`|`) of the bits in each flags value.
+ fn from_iter<T: $crate::__private::core::iter::IntoIterator<Item = Self>>(
+ iterator: T,
+ ) -> Self {
use $crate::__private::core::iter::Extend;
let mut result = Self::empty();
@@ -426,41 +472,64 @@ macro_rules! __impl_public_bitflags_ops {
}
/// Implement constants on the public (user-facing) bitflags type.
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
macro_rules! __impl_public_bitflags_consts {
(
$PublicBitFlags:ident: $T:ty {
$(
- $(#[$attr:ident $($args:tt)*])*
- $Flag:ident = $value:expr;
+ $(#[$inner:ident $($args:tt)*])*
+ const $Flag:tt = $value:expr;
)*
}
) => {
impl $PublicBitFlags {
$(
- $(#[$attr $($args)*])*
- #[allow(
- deprecated,
- non_upper_case_globals,
- )]
- pub const $Flag: Self = Self::from_bits_retain($value);
+ $crate::__bitflags_flag!({
+ name: $Flag,
+ named: {
+ $(#[$inner $($args)*])*
+ #[allow(
+ deprecated,
+ non_upper_case_globals,
+ )]
+ pub const $Flag: Self = Self::from_bits_retain($value);
+ },
+ unnamed: {},
+ });
)*
}
impl $crate::Flags for $PublicBitFlags {
const FLAGS: &'static [$crate::Flag<$PublicBitFlags>] = &[
$(
- __bitflags_expr_safe_attrs!(
- $(#[$attr $($args)*])*
- {
- #[allow(
- deprecated,
- non_upper_case_globals,
- )]
- $crate::Flag::new($crate::__private::core::stringify!($Flag), $PublicBitFlags::$Flag)
- }
- ),
+ $crate::__bitflags_flag!({
+ name: $Flag,
+ named: {
+ $crate::__bitflags_expr_safe_attrs!(
+ $(#[$inner $($args)*])*
+ {
+ #[allow(
+ deprecated,
+ non_upper_case_globals,
+ )]
+ $crate::Flag::new($crate::__private::core::stringify!($Flag), $PublicBitFlags::$Flag)
+ }
+ )
+ },
+ unnamed: {
+ $crate::__bitflags_expr_safe_attrs!(
+ $(#[$inner $($args)*])*
+ {
+ #[allow(
+ deprecated,
+ non_upper_case_globals,
+ )]
+ $crate::Flag::new("", $PublicBitFlags::from_bits_retain($value))
+ }
+ )
+ },
+ }),
)*
];
diff --git a/src/tests.rs b/src/tests.rs
new file mode 100644
index 0000000..ed52ad4
--- /dev/null
+++ b/src/tests.rs
@@ -0,0 +1,131 @@
+mod all;
+mod bits;
+mod complement;
+mod contains;
+mod difference;
+mod empty;
+mod eq;
+mod extend;
+mod flags;
+mod fmt;
+mod from_bits;
+mod from_bits_retain;
+mod from_bits_truncate;
+mod from_name;
+mod insert;
+mod intersection;
+mod intersects;
+mod is_all;
+mod is_empty;
+mod iter;
+mod parser;
+mod remove;
+mod symmetric_difference;
+mod union;
+
+bitflags! {
+ #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+ pub struct TestFlags: u8 {
+ /// 1
+ const A = 1;
+
+ /// 1 << 1
+ const B = 1 << 1;
+
+ /// 1 << 2
+ const C = 1 << 2;
+
+ /// 1 | (1 << 1) | (1 << 2)
+ const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
+ }
+
+ #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+ pub struct TestFlagsInvert: u8 {
+ /// 1 | (1 << 1) | (1 << 2)
+ const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
+
+ /// 1
+ const A = 1;
+
+ /// 1 << 1
+ const B = 1 << 1;
+
+ /// 1 << 2
+ const C = 1 << 2;
+ }
+
+ #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+ pub struct TestZero: u8 {
+ /// 0
+ const ZERO = 0;
+ }
+
+ #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+ pub struct TestZeroOne: u8 {
+ /// 0
+ const ZERO = 0;
+
+ /// 1
+ const ONE = 1;
+ }
+
+ #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+ pub struct TestUnicode: u8 {
+ /// 1
+ const 一 = 1;
+
+ /// 2
+ const 二 = 1 << 1;
+ }
+
+ #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+ pub struct TestEmpty: u8 {}
+
+ #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+ pub struct TestOverlapping: u8 {
+ /// 1 | (1 << 1)
+ const AB = 1 | (1 << 1);
+
+ /// (1 << 1) | (1 << 2)
+ const BC = (1 << 1) | (1 << 2);
+ }
+
+ #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+ pub struct TestOverlappingFull: u8 {
+ /// 1
+ const A = 1;
+
+ /// 1
+ const B = 1;
+
+ /// 1
+ const C = 1;
+
+ /// 2
+ const D = 1 << 1;
+ }
+
+ #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+ pub struct TestExternal: u8 {
+ /// 1
+ const A = 1;
+
+ /// 1 << 1
+ const B = 1 << 1;
+
+ /// 1 << 2
+ const C = 1 << 2;
+
+ /// 1 | (1 << 1) | (1 << 2)
+ const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
+
+ /// External
+ const _ = !0;
+ }
+
+ #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+ pub struct TestExternalFull: u8 {
+ /// External
+ const _ = !0;
+ }
+}
diff --git a/src/tests/all.rs b/src/tests/all.rs
new file mode 100644
index 0000000..cceb93a
--- /dev/null
+++ b/src/tests/all.rs
@@ -0,0 +1,23 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(1 | 1 << 1 | 1 << 2, TestFlags::all);
+
+ case(0, TestZero::all);
+
+ case(0, TestEmpty::all);
+
+ case(!0, TestExternal::all);
+}
+
+#[track_caller]
+fn case<T: Flags>(expected: T::Bits, inherent: impl FnOnce() -> T)
+where
+ <T as Flags>::Bits: std::fmt::Debug + PartialEq,
+{
+ assert_eq!(expected, inherent().bits(), "T::all()");
+ assert_eq!(expected, T::all().bits(), "Flags::all()");
+}
diff --git a/src/tests/bits.rs b/src/tests/bits.rs
new file mode 100644
index 0000000..678f153
--- /dev/null
+++ b/src/tests/bits.rs
@@ -0,0 +1,36 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(0, TestFlags::empty(), TestFlags::bits);
+
+ case(1, TestFlags::A, TestFlags::bits);
+ case(1 | 1 << 1 | 1 << 2, TestFlags::ABC, TestFlags::bits);
+
+ case(!0, TestFlags::from_bits_retain(u8::MAX), TestFlags::bits);
+ case(1 << 3, TestFlags::from_bits_retain(1 << 3), TestFlags::bits);
+
+ case(1 << 3, TestZero::from_bits_retain(1 << 3), TestZero::bits);
+
+ case(1 << 3, TestEmpty::from_bits_retain(1 << 3), TestEmpty::bits);
+
+ case(
+ 1 << 4 | 1 << 6,
+ TestExternal::from_bits_retain(1 << 4 | 1 << 6),
+ TestExternal::bits,
+ );
+}
+
+#[track_caller]
+fn case<T: Flags + std::fmt::Debug>(
+ expected: T::Bits,
+ value: T,
+ inherent: impl FnOnce(&T) -> T::Bits,
+) where
+ T::Bits: std::fmt::Debug + PartialEq,
+{
+ assert_eq!(expected, inherent(&value), "{:?}.bits()", value);
+ assert_eq!(expected, Flags::bits(&value), "Flags::bits({:?})", value);
+}
diff --git a/src/tests/complement.rs b/src/tests/complement.rs
new file mode 100644
index 0000000..ac7a421
--- /dev/null
+++ b/src/tests/complement.rs
@@ -0,0 +1,53 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(0, TestFlags::all(), TestFlags::complement);
+ case(0, TestFlags::from_bits_retain(!0), TestFlags::complement);
+
+ case(1 | 1 << 1, TestFlags::C, TestFlags::complement);
+ case(
+ 1 | 1 << 1,
+ TestFlags::C | TestFlags::from_bits_retain(1 << 3),
+ TestFlags::complement,
+ );
+
+ case(
+ 1 | 1 << 1 | 1 << 2,
+ TestFlags::empty(),
+ TestFlags::complement,
+ );
+ case(
+ 1 | 1 << 1 | 1 << 2,
+ TestFlags::from_bits_retain(1 << 3),
+ TestFlags::complement,
+ );
+
+ case(0, TestZero::empty(), TestZero::complement);
+
+ case(0, TestEmpty::empty(), TestEmpty::complement);
+
+ case(1 << 2, TestOverlapping::AB, TestOverlapping::complement);
+
+ case(!0, TestExternal::empty(), TestExternal::complement);
+}
+
+#[track_caller]
+fn case<T: Flags + std::fmt::Debug + std::ops::Not<Output = T> + Copy>(
+ expected: T::Bits,
+ value: T,
+ inherent: impl FnOnce(T) -> T,
+) where
+ T::Bits: std::fmt::Debug + PartialEq,
+{
+ assert_eq!(expected, inherent(value).bits(), "{:?}.complement()", value);
+ assert_eq!(
+ expected,
+ Flags::complement(value).bits(),
+ "Flags::complement({:?})",
+ value
+ );
+ assert_eq!(expected, (!value).bits(), "!{:?}", value);
+}
diff --git a/src/tests/contains.rs b/src/tests/contains.rs
new file mode 100644
index 0000000..12428dd
--- /dev/null
+++ b/src/tests/contains.rs
@@ -0,0 +1,108 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(
+ TestFlags::empty(),
+ &[
+ (TestFlags::empty(), true),
+ (TestFlags::A, false),
+ (TestFlags::B, false),
+ (TestFlags::C, false),
+ (TestFlags::from_bits_retain(1 << 3), false),
+ ],
+ TestFlags::contains,
+ );
+
+ case(
+ TestFlags::A,
+ &[
+ (TestFlags::empty(), true),
+ (TestFlags::A, true),
+ (TestFlags::B, false),
+ (TestFlags::C, false),
+ (TestFlags::ABC, false),
+ (TestFlags::from_bits_retain(1 << 3), false),
+ (TestFlags::from_bits_retain(1 | (1 << 3)), false),
+ ],
+ TestFlags::contains,
+ );
+
+ case(
+ TestFlags::ABC,
+ &[
+ (TestFlags::empty(), true),
+ (TestFlags::A, true),
+ (TestFlags::B, true),
+ (TestFlags::C, true),
+ (TestFlags::ABC, true),
+ (TestFlags::from_bits_retain(1 << 3), false),
+ ],
+ TestFlags::contains,
+ );
+
+ case(
+ TestFlags::from_bits_retain(1 << 3),
+ &[
+ (TestFlags::empty(), true),
+ (TestFlags::A, false),
+ (TestFlags::B, false),
+ (TestFlags::C, false),
+ (TestFlags::from_bits_retain(1 << 3), true),
+ ],
+ TestFlags::contains,
+ );
+
+ case(
+ TestZero::ZERO,
+ &[(TestZero::ZERO, true)],
+ TestZero::contains,
+ );
+
+ case(
+ TestOverlapping::AB,
+ &[
+ (TestOverlapping::AB, true),
+ (TestOverlapping::BC, false),
+ (TestOverlapping::from_bits_retain(1 << 1), true),
+ ],
+ TestOverlapping::contains,
+ );
+
+ case(
+ TestExternal::all(),
+ &[
+ (TestExternal::A, true),
+ (TestExternal::B, true),
+ (TestExternal::C, true),
+ (TestExternal::from_bits_retain(1 << 5 | 1 << 7), true),
+ ],
+ TestExternal::contains,
+ );
+}
+
+#[track_caller]
+fn case<T: Flags + std::fmt::Debug + Copy>(
+ value: T,
+ inputs: &[(T, bool)],
+ mut inherent: impl FnMut(&T, T) -> bool,
+) {
+ for (input, expected) in inputs {
+ assert_eq!(
+ *expected,
+ inherent(&value, *input),
+ "{:?}.contains({:?})",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ Flags::contains(&value, *input),
+ "Flags::contains({:?}, {:?})",
+ value,
+ input
+ );
+ }
+}
diff --git a/src/tests/difference.rs b/src/tests/difference.rs
new file mode 100644
index 0000000..6ce9c0b
--- /dev/null
+++ b/src/tests/difference.rs
@@ -0,0 +1,92 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(
+ TestFlags::A | TestFlags::B,
+ &[
+ (TestFlags::A, 1 << 1),
+ (TestFlags::B, 1),
+ (TestFlags::from_bits_retain(1 << 3), 1 | 1 << 1),
+ ],
+ TestFlags::difference,
+ );
+
+ case(
+ TestFlags::from_bits_retain(1 | 1 << 3),
+ &[
+ (TestFlags::A, 1 << 3),
+ (TestFlags::from_bits_retain(1 << 3), 1),
+ ],
+ TestFlags::difference,
+ );
+
+ case(
+ TestExternal::from_bits_retain(!0),
+ &[(TestExternal::A, 0b1111_1110)],
+ TestExternal::difference,
+ );
+
+ assert_eq!(
+ 0b1111_1110,
+ (TestExternal::from_bits_retain(!0) & !TestExternal::A).bits()
+ );
+
+ assert_eq!(
+ 0b1111_1110,
+ (TestFlags::from_bits_retain(!0).difference(TestFlags::A)).bits()
+ );
+
+ // The `!` operator unsets bits that don't correspond to known flags
+ assert_eq!(
+ 1 << 1 | 1 << 2,
+ (TestFlags::from_bits_retain(!0) & !TestFlags::A).bits()
+ );
+}
+
+#[track_caller]
+fn case<T: Flags + std::fmt::Debug + std::ops::Sub<Output = T> + std::ops::SubAssign + Copy>(
+ value: T,
+ inputs: &[(T, T::Bits)],
+ mut inherent: impl FnMut(T, T) -> T,
+) where
+ T::Bits: std::fmt::Debug + PartialEq + Copy,
+{
+ for (input, expected) in inputs {
+ assert_eq!(
+ *expected,
+ inherent(value, *input).bits(),
+ "{:?}.difference({:?})",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ Flags::difference(value, *input).bits(),
+ "Flags::difference({:?}, {:?})",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ (value - *input).bits(),
+ "{:?} - {:?}",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ value -= *input;
+ value
+ }
+ .bits(),
+ "{:?} -= {:?}",
+ value,
+ input,
+ );
+ }
+}
diff --git a/src/tests/empty.rs b/src/tests/empty.rs
new file mode 100644
index 0000000..57fb1c7
--- /dev/null
+++ b/src/tests/empty.rs
@@ -0,0 +1,23 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(0, TestFlags::empty);
+
+ case(0, TestZero::empty);
+
+ case(0, TestEmpty::empty);
+
+ case(0, TestExternal::empty);
+}
+
+#[track_caller]
+fn case<T: Flags>(expected: T::Bits, inherent: impl FnOnce() -> T)
+where
+ <T as Flags>::Bits: std::fmt::Debug + PartialEq,
+{
+ assert_eq!(expected, inherent().bits(), "T::empty()");
+ assert_eq!(expected, T::empty().bits(), "Flags::empty()");
+}
diff --git a/src/tests/eq.rs b/src/tests/eq.rs
new file mode 100644
index 0000000..9779af7
--- /dev/null
+++ b/src/tests/eq.rs
@@ -0,0 +1,10 @@
+use super::*;
+
+#[test]
+fn cases() {
+ assert_eq!(TestFlags::empty(), TestFlags::empty());
+ assert_eq!(TestFlags::all(), TestFlags::all());
+
+ assert!(TestFlags::from_bits_retain(1) < TestFlags::from_bits_retain(2));
+ assert!(TestFlags::from_bits_retain(2) > TestFlags::from_bits_retain(1));
+}
diff --git a/src/tests/extend.rs b/src/tests/extend.rs
new file mode 100644
index 0000000..869dc17
--- /dev/null
+++ b/src/tests/extend.rs
@@ -0,0 +1,42 @@
+use super::*;
+
+#[test]
+fn cases() {
+ let mut flags = TestFlags::empty();
+
+ flags.extend(TestFlags::A);
+
+ assert_eq!(TestFlags::A, flags);
+
+ flags.extend(TestFlags::A | TestFlags::B | TestFlags::C);
+
+ assert_eq!(TestFlags::ABC, flags);
+
+ flags.extend(TestFlags::from_bits_retain(1 << 5));
+
+ assert_eq!(TestFlags::ABC | TestFlags::from_bits_retain(1 << 5), flags);
+}
+
+mod external {
+ use super::*;
+
+ #[test]
+ fn cases() {
+ let mut flags = TestExternal::empty();
+
+ flags.extend(TestExternal::A);
+
+ assert_eq!(TestExternal::A, flags);
+
+ flags.extend(TestExternal::A | TestExternal::B | TestExternal::C);
+
+ assert_eq!(TestExternal::ABC, flags);
+
+ flags.extend(TestExternal::from_bits_retain(1 << 5));
+
+ assert_eq!(
+ TestExternal::ABC | TestExternal::from_bits_retain(1 << 5),
+ flags
+ );
+ }
+}
diff --git a/src/tests/flags.rs b/src/tests/flags.rs
new file mode 100644
index 0000000..7a625b3
--- /dev/null
+++ b/src/tests/flags.rs
@@ -0,0 +1,46 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ let flags = TestFlags::FLAGS
+ .iter()
+ .map(|flag| (flag.name(), flag.value().bits()))
+ .collect::<Vec<_>>();
+
+ assert_eq!(
+ vec![
+ ("A", 1u8),
+ ("B", 1 << 1),
+ ("C", 1 << 2),
+ ("ABC", 1 | 1 << 1 | 1 << 2),
+ ],
+ flags,
+ );
+
+ assert_eq!(0, TestEmpty::FLAGS.iter().count());
+}
+
+mod external {
+ use super::*;
+
+ #[test]
+ fn cases() {
+ let flags = TestExternal::FLAGS
+ .iter()
+ .map(|flag| (flag.name(), flag.value().bits()))
+ .collect::<Vec<_>>();
+
+ assert_eq!(
+ vec![
+ ("A", 1u8),
+ ("B", 1 << 1),
+ ("C", 1 << 2),
+ ("ABC", 1 | 1 << 1 | 1 << 2),
+ ("", !0),
+ ],
+ flags,
+ );
+ }
+}
diff --git a/src/tests/fmt.rs b/src/tests/fmt.rs
new file mode 100644
index 0000000..ed45718
--- /dev/null
+++ b/src/tests/fmt.rs
@@ -0,0 +1,97 @@
+use super::*;
+
+#[test]
+fn cases() {
+ case(TestFlags::empty(), "TestFlags(0x0)", "0", "0", "0", "0");
+ case(TestFlags::A, "TestFlags(A)", "1", "1", "1", "1");
+ case(
+ TestFlags::all(),
+ "TestFlags(A | B | C)",
+ "7",
+ "7",
+ "7",
+ "111",
+ );
+ case(
+ TestFlags::from_bits_retain(1 << 3),
+ "TestFlags(0x8)",
+ "8",
+ "8",
+ "10",
+ "1000",
+ );
+ case(
+ TestFlags::A | TestFlags::from_bits_retain(1 << 3),
+ "TestFlags(A | 0x8)",
+ "9",
+ "9",
+ "11",
+ "1001",
+ );
+
+ case(TestZero::ZERO, "TestZero(0x0)", "0", "0", "0", "0");
+ case(
+ TestZero::ZERO | TestZero::from_bits_retain(1),
+ "TestZero(0x1)",
+ "1",
+ "1",
+ "1",
+ "1",
+ );
+
+ case(TestZeroOne::ONE, "TestZeroOne(ONE)", "1", "1", "1", "1");
+
+ case(
+ TestOverlapping::from_bits_retain(1 << 1),
+ "TestOverlapping(0x2)",
+ "2",
+ "2",
+ "2",
+ "10",
+ );
+
+ case(
+ TestExternal::from_bits_retain(1 | 1 << 1 | 1 << 3),
+ "TestExternal(A | B | 0x8)",
+ "B",
+ "b",
+ "13",
+ "1011",
+ );
+
+ case(
+ TestExternal::all(),
+ "TestExternal(A | B | C | 0xf8)",
+ "FF",
+ "ff",
+ "377",
+ "11111111",
+ );
+
+ case(
+ TestExternalFull::all(),
+ "TestExternalFull(0xff)",
+ "FF",
+ "ff",
+ "377",
+ "11111111",
+ );
+}
+
+#[track_caller]
+fn case<
+ T: std::fmt::Debug + std::fmt::UpperHex + std::fmt::LowerHex + std::fmt::Octal + std::fmt::Binary,
+>(
+ value: T,
+ debug: &str,
+ uhex: &str,
+ lhex: &str,
+ oct: &str,
+ bin: &str,
+) {
+ assert_eq!(debug, format!("{:?}", value));
+ assert_eq!(uhex, format!("{:X}", value));
+ assert_eq!(lhex, format!("{:x}", value));
+ assert_eq!(oct, format!("{:o}", value));
+ assert_eq!(bin, format!("{:b}", value));
+}
diff --git a/src/tests/from_bits.rs b/src/tests/from_bits.rs
new file mode 100644
index 0000000..dada9af
--- /dev/null
+++ b/src/tests/from_bits.rs
@@ -0,0 +1,45 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(Some(0), 0, TestFlags::from_bits);
+ case(Some(1), 1, TestFlags::from_bits);
+ case(
+ Some(1 | 1 << 1 | 1 << 2),
+ 1 | 1 << 1 | 1 << 2,
+ TestFlags::from_bits,
+ );
+
+ case(None, 1 << 3, TestFlags::from_bits);
+ case(None, 1 | 1 << 3, TestFlags::from_bits);
+
+ case(Some(1 | 1 << 1), 1 | 1 << 1, TestOverlapping::from_bits);
+
+ case(Some(1 << 1), 1 << 1, TestOverlapping::from_bits);
+
+ case(Some(1 << 5), 1 << 5, TestExternal::from_bits);
+}
+
+#[track_caller]
+fn case<T: Flags>(
+ expected: Option<T::Bits>,
+ input: T::Bits,
+ inherent: impl FnOnce(T::Bits) -> Option<T>,
+) where
+ <T as Flags>::Bits: std::fmt::Debug + PartialEq,
+{
+ assert_eq!(
+ expected,
+ inherent(input).map(|f| f.bits()),
+ "T::from_bits({:?})",
+ input
+ );
+ assert_eq!(
+ expected,
+ T::from_bits(input).map(|f| f.bits()),
+ "Flags::from_bits({:?})",
+ input
+ );
+}
diff --git a/src/tests/from_bits_retain.rs b/src/tests/from_bits_retain.rs
new file mode 100644
index 0000000..1ae28a6
--- /dev/null
+++ b/src/tests/from_bits_retain.rs
@@ -0,0 +1,38 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(0, TestFlags::from_bits_retain);
+ case(1, TestFlags::from_bits_retain);
+ case(1 | 1 << 1 | 1 << 2, TestFlags::from_bits_retain);
+
+ case(1 << 3, TestFlags::from_bits_retain);
+ case(1 | 1 << 3, TestFlags::from_bits_retain);
+
+ case(1 | 1 << 1, TestOverlapping::from_bits_retain);
+
+ case(1 << 1, TestOverlapping::from_bits_retain);
+
+ case(1 << 5, TestExternal::from_bits_retain);
+}
+
+#[track_caller]
+fn case<T: Flags>(input: T::Bits, inherent: impl FnOnce(T::Bits) -> T)
+where
+ <T as Flags>::Bits: std::fmt::Debug + PartialEq,
+{
+ assert_eq!(
+ input,
+ inherent(input).bits(),
+ "T::from_bits_retain({:?})",
+ input
+ );
+ assert_eq!(
+ input,
+ T::from_bits_retain(input).bits(),
+ "Flags::from_bits_retain({:?})",
+ input
+ );
+}
diff --git a/src/tests/from_bits_truncate.rs b/src/tests/from_bits_truncate.rs
new file mode 100644
index 0000000..e4f3e53
--- /dev/null
+++ b/src/tests/from_bits_truncate.rs
@@ -0,0 +1,42 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(0, 0, TestFlags::from_bits_truncate);
+ case(1, 1, TestFlags::from_bits_truncate);
+ case(
+ 1 | 1 << 1 | 1 << 2,
+ 1 | 1 << 1 | 1 << 2,
+ TestFlags::from_bits_truncate,
+ );
+
+ case(0, 1 << 3, TestFlags::from_bits_truncate);
+ case(1, 1 | 1 << 3, TestFlags::from_bits_truncate);
+
+ case(1 | 1 << 1, 1 | 1 << 1, TestOverlapping::from_bits_truncate);
+
+ case(1 << 1, 1 << 1, TestOverlapping::from_bits_truncate);
+
+ case(1 << 5, 1 << 5, TestExternal::from_bits_truncate);
+}
+
+#[track_caller]
+fn case<T: Flags>(expected: T::Bits, input: T::Bits, inherent: impl FnOnce(T::Bits) -> T)
+where
+ <T as Flags>::Bits: std::fmt::Debug + PartialEq,
+{
+ assert_eq!(
+ expected,
+ inherent(input).bits(),
+ "T::from_bits_truncate({:?})",
+ input
+ );
+ assert_eq!(
+ expected,
+ T::from_bits_truncate(input).bits(),
+ "Flags::from_bits_truncate({:?})",
+ input
+ );
+}
diff --git a/src/tests/from_name.rs b/src/tests/from_name.rs
new file mode 100644
index 0000000..1d9a4e4
--- /dev/null
+++ b/src/tests/from_name.rs
@@ -0,0 +1,42 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(Some(1), "A", TestFlags::from_name);
+ case(Some(1 << 1), "B", TestFlags::from_name);
+ case(Some(1 | 1 << 1 | 1 << 2), "ABC", TestFlags::from_name);
+
+ case(None, "", TestFlags::from_name);
+ case(None, "a", TestFlags::from_name);
+ case(None, "0x1", TestFlags::from_name);
+ case(None, "A | B", TestFlags::from_name);
+
+ case(Some(0), "ZERO", TestZero::from_name);
+
+ case(Some(2), "二", TestUnicode::from_name);
+
+ case(None, "_", TestExternal::from_name);
+
+ case(None, "", TestExternal::from_name);
+}
+
+#[track_caller]
+fn case<T: Flags>(expected: Option<T::Bits>, input: &str, inherent: impl FnOnce(&str) -> Option<T>)
+where
+ <T as Flags>::Bits: std::fmt::Debug + PartialEq,
+{
+ assert_eq!(
+ expected,
+ inherent(input).map(|f| f.bits()),
+ "T::from_name({:?})",
+ input
+ );
+ assert_eq!(
+ expected,
+ T::from_name(input).map(|f| f.bits()),
+ "Flags::from_name({:?})",
+ input
+ );
+}
diff --git a/src/tests/insert.rs b/src/tests/insert.rs
new file mode 100644
index 0000000..b18cd17
--- /dev/null
+++ b/src/tests/insert.rs
@@ -0,0 +1,91 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(
+ TestFlags::empty(),
+ &[
+ (TestFlags::A, 1),
+ (TestFlags::A | TestFlags::B, 1 | 1 << 1),
+ (TestFlags::empty(), 0),
+ (TestFlags::from_bits_retain(1 << 3), 1 << 3),
+ ],
+ TestFlags::insert,
+ TestFlags::set,
+ );
+
+ case(
+ TestFlags::A,
+ &[
+ (TestFlags::A, 1),
+ (TestFlags::empty(), 1),
+ (TestFlags::B, 1 | 1 << 1),
+ ],
+ TestFlags::insert,
+ TestFlags::set,
+ );
+}
+
+#[track_caller]
+fn case<T: Flags + std::fmt::Debug + Copy>(
+ value: T,
+ inputs: &[(T, T::Bits)],
+ mut inherent_insert: impl FnMut(&mut T, T),
+ mut inherent_set: impl FnMut(&mut T, T, bool),
+) where
+ T::Bits: std::fmt::Debug + PartialEq + Copy,
+{
+ for (input, expected) in inputs {
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ inherent_insert(&mut value, *input);
+ value
+ }
+ .bits(),
+ "{:?}.insert({:?})",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ Flags::insert(&mut value, *input);
+ value
+ }
+ .bits(),
+ "Flags::insert({:?}, {:?})",
+ value,
+ input
+ );
+
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ inherent_set(&mut value, *input, true);
+ value
+ }
+ .bits(),
+ "{:?}.set({:?}, true)",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ Flags::set(&mut value, *input, true);
+ value
+ }
+ .bits(),
+ "Flags::set({:?}, {:?}, true)",
+ value,
+ input
+ );
+ }
+}
diff --git a/src/tests/intersection.rs b/src/tests/intersection.rs
new file mode 100644
index 0000000..10a8ae9
--- /dev/null
+++ b/src/tests/intersection.rs
@@ -0,0 +1,79 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(
+ TestFlags::empty(),
+ &[(TestFlags::empty(), 0), (TestFlags::all(), 0)],
+ TestFlags::intersection,
+ );
+
+ case(
+ TestFlags::all(),
+ &[
+ (TestFlags::all(), 1 | 1 << 1 | 1 << 2),
+ (TestFlags::A, 1),
+ (TestFlags::from_bits_retain(1 << 3), 0),
+ ],
+ TestFlags::intersection,
+ );
+
+ case(
+ TestFlags::from_bits_retain(1 << 3),
+ &[(TestFlags::from_bits_retain(1 << 3), 1 << 3)],
+ TestFlags::intersection,
+ );
+
+ case(
+ TestOverlapping::AB,
+ &[(TestOverlapping::BC, 1 << 1)],
+ TestOverlapping::intersection,
+ );
+}
+
+#[track_caller]
+fn case<T: Flags + std::fmt::Debug + std::ops::BitAnd<Output = T> + std::ops::BitAndAssign + Copy>(
+ value: T,
+ inputs: &[(T, T::Bits)],
+ mut inherent: impl FnMut(T, T) -> T,
+) where
+ T::Bits: std::fmt::Debug + PartialEq + Copy,
+{
+ for (input, expected) in inputs {
+ assert_eq!(
+ *expected,
+ inherent(value, *input).bits(),
+ "{:?}.intersection({:?})",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ Flags::intersection(value, *input).bits(),
+ "Flags::intersection({:?}, {:?})",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ (value & *input).bits(),
+ "{:?} & {:?}",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ value &= *input;
+ value
+ }
+ .bits(),
+ "{:?} &= {:?}",
+ value,
+ input,
+ );
+ }
+}
diff --git a/src/tests/intersects.rs b/src/tests/intersects.rs
new file mode 100644
index 0000000..fe90798
--- /dev/null
+++ b/src/tests/intersects.rs
@@ -0,0 +1,91 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(
+ TestFlags::empty(),
+ &[
+ (TestFlags::empty(), false),
+ (TestFlags::A, false),
+ (TestFlags::B, false),
+ (TestFlags::C, false),
+ (TestFlags::from_bits_retain(1 << 3), false),
+ ],
+ TestFlags::intersects,
+ );
+
+ case(
+ TestFlags::A,
+ &[
+ (TestFlags::empty(), false),
+ (TestFlags::A, true),
+ (TestFlags::B, false),
+ (TestFlags::C, false),
+ (TestFlags::ABC, true),
+ (TestFlags::from_bits_retain(1 << 3), false),
+ (TestFlags::from_bits_retain(1 | (1 << 3)), true),
+ ],
+ TestFlags::intersects,
+ );
+
+ case(
+ TestFlags::ABC,
+ &[
+ (TestFlags::empty(), false),
+ (TestFlags::A, true),
+ (TestFlags::B, true),
+ (TestFlags::C, true),
+ (TestFlags::ABC, true),
+ (TestFlags::from_bits_retain(1 << 3), false),
+ ],
+ TestFlags::intersects,
+ );
+
+ case(
+ TestFlags::from_bits_retain(1 << 3),
+ &[
+ (TestFlags::empty(), false),
+ (TestFlags::A, false),
+ (TestFlags::B, false),
+ (TestFlags::C, false),
+ (TestFlags::from_bits_retain(1 << 3), true),
+ ],
+ TestFlags::intersects,
+ );
+
+ case(
+ TestOverlapping::AB,
+ &[
+ (TestOverlapping::AB, true),
+ (TestOverlapping::BC, true),
+ (TestOverlapping::from_bits_retain(1 << 1), true),
+ ],
+ TestOverlapping::intersects,
+ );
+}
+
+#[track_caller]
+fn case<T: Flags + std::fmt::Debug + Copy>(
+ value: T,
+ inputs: &[(T, bool)],
+ mut inherent: impl FnMut(&T, T) -> bool,
+) {
+ for (input, expected) in inputs {
+ assert_eq!(
+ *expected,
+ inherent(&value, *input),
+ "{:?}.intersects({:?})",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ Flags::intersects(&value, *input),
+ "Flags::intersects({:?}, {:?})",
+ value,
+ input
+ );
+ }
+}
diff --git a/src/tests/is_all.rs b/src/tests/is_all.rs
new file mode 100644
index 0000000..382a458
--- /dev/null
+++ b/src/tests/is_all.rs
@@ -0,0 +1,32 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(false, TestFlags::empty(), TestFlags::is_all);
+ case(false, TestFlags::A, TestFlags::is_all);
+
+ case(true, TestFlags::ABC, TestFlags::is_all);
+
+ case(
+ true,
+ TestFlags::ABC | TestFlags::from_bits_retain(1 << 3),
+ TestFlags::is_all,
+ );
+
+ case(true, TestZero::empty(), TestZero::is_all);
+
+ case(true, TestEmpty::empty(), TestEmpty::is_all);
+}
+
+#[track_caller]
+fn case<T: Flags + std::fmt::Debug>(expected: bool, value: T, inherent: impl FnOnce(&T) -> bool) {
+ assert_eq!(expected, inherent(&value), "{:?}.is_all()", value);
+ assert_eq!(
+ expected,
+ Flags::is_all(&value),
+ "Flags::is_all({:?})",
+ value
+ );
+}
diff --git a/src/tests/is_empty.rs b/src/tests/is_empty.rs
new file mode 100644
index 0000000..92165f1
--- /dev/null
+++ b/src/tests/is_empty.rs
@@ -0,0 +1,31 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(true, TestFlags::empty(), TestFlags::is_empty);
+
+ case(false, TestFlags::A, TestFlags::is_empty);
+ case(false, TestFlags::ABC, TestFlags::is_empty);
+ case(
+ false,
+ TestFlags::from_bits_retain(1 << 3),
+ TestFlags::is_empty,
+ );
+
+ case(true, TestZero::empty(), TestZero::is_empty);
+
+ case(true, TestEmpty::empty(), TestEmpty::is_empty);
+}
+
+#[track_caller]
+fn case<T: Flags + std::fmt::Debug>(expected: bool, value: T, inherent: impl FnOnce(&T) -> bool) {
+ assert_eq!(expected, inherent(&value), "{:?}.is_empty()", value);
+ assert_eq!(
+ expected,
+ Flags::is_empty(&value),
+ "Flags::is_empty({:?})",
+ value
+ );
+}
diff --git a/src/tests/iter.rs b/src/tests/iter.rs
new file mode 100644
index 0000000..54b1d27
--- /dev/null
+++ b/src/tests/iter.rs
@@ -0,0 +1,209 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+#[cfg(not(miri))] // Very slow in miri
+fn roundtrip() {
+ for a in 0u8..=255 {
+ for b in 0u8..=255 {
+ let f = TestFlags::from_bits_retain(a | b);
+
+ assert_eq!(f, f.iter().collect::<TestFlags>());
+ assert_eq!(
+ TestFlags::from_bits_truncate(f.bits()),
+ f.iter_names().map(|(_, f)| f).collect::<TestFlags>()
+ );
+
+ let f = TestExternal::from_bits_retain(a | b);
+
+ assert_eq!(f, f.iter().collect::<TestExternal>());
+ }
+ }
+}
+
+mod collect {
+ use super::*;
+
+ #[test]
+ fn cases() {
+ assert_eq!(0, [].into_iter().collect::<TestFlags>().bits());
+
+ assert_eq!(1, [TestFlags::A,].into_iter().collect::<TestFlags>().bits());
+
+ assert_eq!(
+ 1 | 1 << 1 | 1 << 2,
+ [TestFlags::A, TestFlags::B | TestFlags::C,]
+ .into_iter()
+ .collect::<TestFlags>()
+ .bits()
+ );
+
+ assert_eq!(
+ 1 | 1 << 3,
+ [
+ TestFlags::from_bits_retain(1 << 3),
+ TestFlags::empty(),
+ TestFlags::A,
+ ]
+ .into_iter()
+ .collect::<TestFlags>()
+ .bits()
+ );
+
+ assert_eq!(
+ 1 << 5 | 1 << 7,
+ [
+ TestExternal::empty(),
+ TestExternal::from_bits_retain(1 << 5),
+ TestExternal::from_bits_retain(1 << 7),
+ ]
+ .into_iter()
+ .collect::<TestExternal>()
+ .bits()
+ );
+ }
+}
+
+mod iter {
+ use super::*;
+
+ #[test]
+ fn cases() {
+ case(&[], TestFlags::empty(), TestFlags::iter);
+
+ case(&[1], TestFlags::A, TestFlags::iter);
+ case(&[1, 1 << 1], TestFlags::A | TestFlags::B, TestFlags::iter);
+ case(
+ &[1, 1 << 1, 1 << 3],
+ TestFlags::A | TestFlags::B | TestFlags::from_bits_retain(1 << 3),
+ TestFlags::iter,
+ );
+
+ case(&[1, 1 << 1, 1 << 2], TestFlags::ABC, TestFlags::iter);
+ case(
+ &[1, 1 << 1, 1 << 2, 1 << 3],
+ TestFlags::ABC | TestFlags::from_bits_retain(1 << 3),
+ TestFlags::iter,
+ );
+
+ case(
+ &[1 | 1 << 1 | 1 << 2],
+ TestFlagsInvert::ABC,
+ TestFlagsInvert::iter,
+ );
+
+ case(&[], TestZero::ZERO, TestZero::iter);
+
+ case(
+ &[1, 1 << 1, 1 << 2, 0b1111_1000],
+ TestExternal::all(),
+ TestExternal::iter,
+ );
+ }
+
+ #[track_caller]
+ fn case<T: Flags + std::fmt::Debug + IntoIterator<Item = T> + Copy>(
+ expected: &[T::Bits],
+ value: T,
+ inherent: impl FnOnce(&T) -> crate::iter::Iter<T>,
+ ) where
+ T::Bits: std::fmt::Debug + PartialEq,
+ {
+ assert_eq!(
+ expected,
+ inherent(&value).map(|f| f.bits()).collect::<Vec<_>>(),
+ "{:?}.iter()",
+ value
+ );
+ assert_eq!(
+ expected,
+ Flags::iter(&value).map(|f| f.bits()).collect::<Vec<_>>(),
+ "Flags::iter({:?})",
+ value
+ );
+ assert_eq!(
+ expected,
+ value.into_iter().map(|f| f.bits()).collect::<Vec<_>>(),
+ "{:?}.into_iter()",
+ value
+ );
+ }
+}
+
+mod iter_names {
+ use super::*;
+
+ #[test]
+ fn cases() {
+ case(&[], TestFlags::empty(), TestFlags::iter_names);
+
+ case(&[("A", 1)], TestFlags::A, TestFlags::iter_names);
+ case(
+ &[("A", 1), ("B", 1 << 1)],
+ TestFlags::A | TestFlags::B,
+ TestFlags::iter_names,
+ );
+ case(
+ &[("A", 1), ("B", 1 << 1)],
+ TestFlags::A | TestFlags::B | TestFlags::from_bits_retain(1 << 3),
+ TestFlags::iter_names,
+ );
+
+ case(
+ &[("A", 1), ("B", 1 << 1), ("C", 1 << 2)],
+ TestFlags::ABC,
+ TestFlags::iter_names,
+ );
+ case(
+ &[("A", 1), ("B", 1 << 1), ("C", 1 << 2)],
+ TestFlags::ABC | TestFlags::from_bits_retain(1 << 3),
+ TestFlags::iter_names,
+ );
+
+ case(
+ &[("ABC", 1 | 1 << 1 | 1 << 2)],
+ TestFlagsInvert::ABC,
+ TestFlagsInvert::iter_names,
+ );
+
+ case(&[], TestZero::ZERO, TestZero::iter_names);
+
+ case(
+ &[("A", 1)],
+ TestOverlappingFull::A,
+ TestOverlappingFull::iter_names,
+ );
+ case(
+ &[("A", 1), ("D", 1 << 1)],
+ TestOverlappingFull::A | TestOverlappingFull::D,
+ TestOverlappingFull::iter_names,
+ );
+ }
+
+ #[track_caller]
+ fn case<T: Flags + std::fmt::Debug>(
+ expected: &[(&'static str, T::Bits)],
+ value: T,
+ inherent: impl FnOnce(&T) -> crate::iter::IterNames<T>,
+ ) where
+ T::Bits: std::fmt::Debug + PartialEq,
+ {
+ assert_eq!(
+ expected,
+ inherent(&value)
+ .map(|(n, f)| (n, f.bits()))
+ .collect::<Vec<_>>(),
+ "{:?}.iter_names()",
+ value
+ );
+ assert_eq!(
+ expected,
+ Flags::iter_names(&value)
+ .map(|(n, f)| (n, f.bits()))
+ .collect::<Vec<_>>(),
+ "Flags::iter_names({:?})",
+ value
+ );
+ }
+}
diff --git a/src/tests/parser.rs b/src/tests/parser.rs
new file mode 100644
index 0000000..b370785
--- /dev/null
+++ b/src/tests/parser.rs
@@ -0,0 +1,116 @@
+use super::*;
+
+use crate::{
+ parser::{from_str, to_writer},
+ Flags,
+};
+
+#[test]
+#[cfg(not(miri))] // Very slow in miri
+fn roundtrip() {
+ let mut s = String::new();
+
+ for a in 0u8..=255 {
+ for b in 0u8..=255 {
+ let f = TestFlags::from_bits_retain(a | b);
+
+ s.clear();
+ to_writer(&f, &mut s).unwrap();
+
+ assert_eq!(f, from_str::<TestFlags>(&s).unwrap());
+ }
+ }
+}
+
+mod from_str {
+ use super::*;
+
+ #[test]
+ fn valid() {
+ assert_eq!(0, from_str::<TestFlags>("").unwrap().bits());
+
+ assert_eq!(1, from_str::<TestFlags>("A").unwrap().bits());
+ assert_eq!(1, from_str::<TestFlags>(" A ").unwrap().bits());
+ assert_eq!(
+ 1 | 1 << 1 | 1 << 2,
+ from_str::<TestFlags>("A | B | C").unwrap().bits()
+ );
+ assert_eq!(
+ 1 | 1 << 1 | 1 << 2,
+ from_str::<TestFlags>("A\n|\tB\r\n| C ").unwrap().bits()
+ );
+ assert_eq!(
+ 1 | 1 << 1 | 1 << 2,
+ from_str::<TestFlags>("A|B|C").unwrap().bits()
+ );
+
+ assert_eq!(1 << 3, from_str::<TestFlags>("0x8").unwrap().bits());
+ assert_eq!(1 | 1 << 3, from_str::<TestFlags>("A | 0x8").unwrap().bits());
+ assert_eq!(
+ 1 | 1 << 1 | 1 << 3,
+ from_str::<TestFlags>("0x1 | 0x8 | B").unwrap().bits()
+ );
+
+ assert_eq!(
+ 1 | 1 << 1,
+ from_str::<TestUnicode>("一 | 二").unwrap().bits()
+ );
+ }
+
+ #[test]
+ fn invalid() {
+ assert!(from_str::<TestFlags>("a")
+ .unwrap_err()
+ .to_string()
+ .starts_with("unrecognized named flag"));
+ assert!(from_str::<TestFlags>("A & B")
+ .unwrap_err()
+ .to_string()
+ .starts_with("unrecognized named flag"));
+
+ assert!(from_str::<TestFlags>("0xg")
+ .unwrap_err()
+ .to_string()
+ .starts_with("invalid hex flag"));
+ assert!(from_str::<TestFlags>("0xffffffffffff")
+ .unwrap_err()
+ .to_string()
+ .starts_with("invalid hex flag"));
+ }
+}
+
+mod to_writer {
+ use super::*;
+
+ #[test]
+ fn cases() {
+ assert_eq!("", write(TestFlags::empty()));
+ assert_eq!("A", write(TestFlags::A));
+ assert_eq!("A | B | C", write(TestFlags::all()));
+ assert_eq!("0x8", write(TestFlags::from_bits_retain(1 << 3)));
+ assert_eq!(
+ "A | 0x8",
+ write(TestFlags::A | TestFlags::from_bits_retain(1 << 3))
+ );
+
+ assert_eq!("", write(TestZero::ZERO));
+
+ assert_eq!("ABC", write(TestFlagsInvert::all()));
+
+ assert_eq!("A", write(TestOverlappingFull::C));
+ assert_eq!(
+ "A | D",
+ write(TestOverlappingFull::C | TestOverlappingFull::D)
+ );
+ }
+
+ fn write<F: Flags>(value: F) -> String
+ where
+ F::Bits: crate::parser::WriteHex,
+ {
+ let mut s = String::new();
+
+ to_writer(&value, &mut s).unwrap();
+ s
+ }
+}
diff --git a/src/tests/remove.rs b/src/tests/remove.rs
new file mode 100644
index 0000000..574b1ed
--- /dev/null
+++ b/src/tests/remove.rs
@@ -0,0 +1,100 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(
+ TestFlags::empty(),
+ &[
+ (TestFlags::A, 0),
+ (TestFlags::empty(), 0),
+ (TestFlags::from_bits_retain(1 << 3), 0),
+ ],
+ TestFlags::remove,
+ TestFlags::set,
+ );
+
+ case(
+ TestFlags::A,
+ &[
+ (TestFlags::A, 0),
+ (TestFlags::empty(), 1),
+ (TestFlags::B, 1),
+ ],
+ TestFlags::remove,
+ TestFlags::set,
+ );
+
+ case(
+ TestFlags::ABC,
+ &[
+ (TestFlags::A, 1 << 1 | 1 << 2),
+ (TestFlags::A | TestFlags::C, 1 << 1),
+ ],
+ TestFlags::remove,
+ TestFlags::set,
+ );
+}
+
+#[track_caller]
+fn case<T: Flags + std::fmt::Debug + Copy>(
+ value: T,
+ inputs: &[(T, T::Bits)],
+ mut inherent_remove: impl FnMut(&mut T, T),
+ mut inherent_set: impl FnMut(&mut T, T, bool),
+) where
+ T::Bits: std::fmt::Debug + PartialEq + Copy,
+{
+ for (input, expected) in inputs {
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ inherent_remove(&mut value, *input);
+ value
+ }
+ .bits(),
+ "{:?}.remove({:?})",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ Flags::remove(&mut value, *input);
+ value
+ }
+ .bits(),
+ "Flags::remove({:?}, {:?})",
+ value,
+ input
+ );
+
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ inherent_set(&mut value, *input, false);
+ value
+ }
+ .bits(),
+ "{:?}.set({:?}, false)",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ Flags::set(&mut value, *input, false);
+ value
+ }
+ .bits(),
+ "Flags::set({:?}, {:?}, false)",
+ value,
+ input
+ );
+ }
+}
diff --git a/src/tests/symmetric_difference.rs b/src/tests/symmetric_difference.rs
new file mode 100644
index 0000000..75e9123
--- /dev/null
+++ b/src/tests/symmetric_difference.rs
@@ -0,0 +1,110 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(
+ TestFlags::empty(),
+ &[
+ (TestFlags::empty(), 0),
+ (TestFlags::all(), 1 | 1 << 1 | 1 << 2),
+ (TestFlags::from_bits_retain(1 << 3), 1 << 3),
+ ],
+ TestFlags::symmetric_difference,
+ TestFlags::toggle,
+ );
+
+ case(
+ TestFlags::A,
+ &[
+ (TestFlags::empty(), 1),
+ (TestFlags::A, 0),
+ (TestFlags::all(), 1 << 1 | 1 << 2),
+ ],
+ TestFlags::symmetric_difference,
+ TestFlags::toggle,
+ );
+
+ case(
+ TestFlags::A | TestFlags::B | TestFlags::from_bits_retain(1 << 3),
+ &[
+ (TestFlags::ABC, 1 << 2 | 1 << 3),
+ (TestFlags::from_bits_retain(1 << 3), 1 | 1 << 1),
+ ],
+ TestFlags::symmetric_difference,
+ TestFlags::toggle,
+ );
+}
+
+#[track_caller]
+fn case<T: Flags + std::fmt::Debug + std::ops::BitXor<Output = T> + std::ops::BitXorAssign + Copy>(
+ value: T,
+ inputs: &[(T, T::Bits)],
+ mut inherent_sym_diff: impl FnMut(T, T) -> T,
+ mut inherent_toggle: impl FnMut(&mut T, T),
+) where
+ T::Bits: std::fmt::Debug + PartialEq + Copy,
+{
+ for (input, expected) in inputs {
+ assert_eq!(
+ *expected,
+ inherent_sym_diff(value, *input).bits(),
+ "{:?}.symmetric_difference({:?})",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ Flags::symmetric_difference(value, *input).bits(),
+ "Flags::symmetric_difference({:?}, {:?})",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ (value ^ *input).bits(),
+ "{:?} ^ {:?}",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ value ^= *input;
+ value
+ }
+ .bits(),
+ "{:?} ^= {:?}",
+ value,
+ input,
+ );
+
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ inherent_toggle(&mut value, *input);
+ value
+ }
+ .bits(),
+ "{:?}.toggle({:?})",
+ value,
+ input,
+ );
+
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ Flags::toggle(&mut value, *input);
+ value
+ }
+ .bits(),
+ "{:?}.toggle({:?})",
+ value,
+ input,
+ );
+ }
+}
diff --git a/src/tests/union.rs b/src/tests/union.rs
new file mode 100644
index 0000000..6190681
--- /dev/null
+++ b/src/tests/union.rs
@@ -0,0 +1,71 @@
+use super::*;
+
+use crate::Flags;
+
+#[test]
+fn cases() {
+ case(
+ TestFlags::empty(),
+ &[
+ (TestFlags::A, 1),
+ (TestFlags::all(), 1 | 1 << 1 | 1 << 2),
+ (TestFlags::empty(), 0),
+ (TestFlags::from_bits_retain(1 << 3), 1 << 3),
+ ],
+ TestFlags::union,
+ );
+
+ case(
+ TestFlags::A | TestFlags::C,
+ &[
+ (TestFlags::A | TestFlags::B, 1 | 1 << 1 | 1 << 2),
+ (TestFlags::A, 1 | 1 << 2),
+ ],
+ TestFlags::union,
+ );
+}
+
+#[track_caller]
+fn case<T: Flags + std::fmt::Debug + std::ops::BitOr<Output = T> + std::ops::BitOrAssign + Copy>(
+ value: T,
+ inputs: &[(T, T::Bits)],
+ mut inherent: impl FnMut(T, T) -> T,
+) where
+ T::Bits: std::fmt::Debug + PartialEq + Copy,
+{
+ for (input, expected) in inputs {
+ assert_eq!(
+ *expected,
+ inherent(value, *input).bits(),
+ "{:?}.union({:?})",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ Flags::union(value, *input).bits(),
+ "Flags::union({:?}, {:?})",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ (value | *input).bits(),
+ "{:?} | {:?}",
+ value,
+ input
+ );
+ assert_eq!(
+ *expected,
+ {
+ let mut value = value;
+ value |= *input;
+ value
+ }
+ .bits(),
+ "{:?} |= {:?}",
+ value,
+ input,
+ );
+ }
+}
diff --git a/src/traits.rs b/src/traits.rs
index f8fc757..2823514 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -1,60 +1,164 @@
-use core::{fmt, ops::{BitAnd, BitOr, BitXor, Not}};
-
-use crate::{parser::{ParseError, ParseHex, WriteHex}, iter};
-
-/// Metadata for an individual flag.
+use core::{
+ fmt,
+ ops::{BitAnd, BitOr, BitXor, Not},
+};
+
+use crate::{
+ iter,
+ parser::{ParseError, ParseHex, WriteHex},
+};
+
+/**
+A defined flags value that may be named or unnamed.
+*/
pub struct Flag<B> {
name: &'static str,
value: B,
}
impl<B> Flag<B> {
- /// Create a new flag with the given name and value.
+ /**
+ Define a flag.
+
+ If `name` is non-empty then the flag is named, otherwise it's unnamed.
+ */
pub const fn new(name: &'static str, value: B) -> Self {
Flag { name, value }
}
- /// Get the name of this flag.
+ /**
+ Get the name of this flag.
+
+ If the flag is unnamed then the returned string will be empty.
+ */
pub const fn name(&self) -> &'static str {
self.name
}
- /// Get the value of this flag.
+ /**
+ Get the flags value of this flag.
+ */
pub const fn value(&self) -> &B {
&self.value
}
+
+ /**
+ Whether the flag is named.
+
+ If [`Flag::name`] returns a non-empty string then this method will return `true`.
+ */
+ pub const fn is_named(&self) -> bool {
+ !self.name.is_empty()
+ }
+
+ /**
+ Whether the flag is unnamed.
+
+ If [`Flag::name`] returns a non-empty string then this method will return `false`.
+ */
+ pub const fn is_unnamed(&self) -> bool {
+ self.name.is_empty()
+ }
}
-/// A set of flags.
-///
-/// This trait is automatically implemented for flags types defined using the `bitflags!` macro.
-/// It can also be implemented manually for custom flags types.
+/**
+A set of defined flags using a bits type as storage.
+
+## Implementing `Flags`
+
+This trait is implemented by the [`bitflags`](macro.bitflags.html) macro:
+
+```
+use bitflags::bitflags;
+
+bitflags! {
+ struct MyFlags: u8 {
+ const A = 1;
+ const B = 1 << 1;
+ }
+}
+```
+
+It can also be implemented manually:
+
+```
+use bitflags::{Flag, Flags};
+
+struct MyFlags(u8);
+
+impl Flags for MyFlags {
+ const FLAGS: &'static [Flag<Self>] = &[
+ Flag::new("A", MyFlags(1)),
+ Flag::new("B", MyFlags(1 << 1)),
+ ];
+
+ type Bits = u8;
+
+ fn from_bits_retain(bits: Self::Bits) -> Self {
+ MyFlags(bits)
+ }
+
+ fn bits(&self) -> Self::Bits {
+ self.0
+ }
+}
+```
+
+## Using `Flags`
+
+The `Flags` trait can be used generically to work with any flags types. In this example,
+we can count the number of defined named flags:
+
+```
+# use bitflags::{bitflags, Flags};
+fn defined_flags<F: Flags>() -> usize {
+ F::FLAGS.iter().filter(|f| f.is_named()).count()
+}
+
+bitflags! {
+ struct MyFlags: u8 {
+ const A = 1;
+ const B = 1 << 1;
+ const C = 1 << 2;
+
+ const _ = !0;
+ }
+}
+
+assert_eq!(3, defined_flags::<MyFlags>());
+```
+*/
pub trait Flags: Sized + 'static {
- /// The set of available flags and their names.
+ /// The set of defined flags.
const FLAGS: &'static [Flag<Self>];
- /// The underlying storage type.
+ /// The underlying bits type.
type Bits: Bits;
- /// Returns an empty set of flags.
+ /// Get a flags value with all bits unset.
fn empty() -> Self {
Self::from_bits_retain(Self::Bits::EMPTY)
}
- /// Returns the set containing all flags.
+ /// Get a flags value with all known bits set.
fn all() -> Self {
- Self::from_bits_truncate(Self::Bits::ALL)
+ let mut truncated = Self::Bits::EMPTY;
+
+ for flag in Self::FLAGS.iter() {
+ truncated = truncated | flag.value().bits();
+ }
+
+ Self::from_bits_retain(truncated)
}
- /// Returns the raw value of the flags currently stored.
+ /// Get the underlying bits value.
+ ///
+ /// The returned value is exactly the bits set in this flags value.
fn bits(&self) -> Self::Bits;
- /// Convert from underlying bit representation, unless that
- /// representation contains bits that do not correspond to a flag.
- ///
- /// Note that each [multi-bit flag] is treated as a unit for this comparison.
+ /// Convert from a bits value.
///
- /// [multi-bit flag]: index.html#multi-bit-flags
+ /// This method will return `None` if any unknown bits are set.
fn from_bits(bits: Self::Bits) -> Option<Self> {
let truncated = Self::from_bits_truncate(bits);
@@ -65,68 +169,62 @@ pub trait Flags: Sized + 'static {
}
}
- /// Convert from underlying bit representation, dropping any bits
- /// that do not correspond to flags.
- ///
- /// Note that each [multi-bit flag] is treated as a unit for this comparison.
- ///
- /// [multi-bit flag]: index.html#multi-bit-flags
+ /// Convert from a bits value, unsetting any unknown bits.
fn from_bits_truncate(bits: Self::Bits) -> Self {
- if bits == Self::Bits::EMPTY {
- return Self::empty();
- }
-
- let mut truncated = Self::Bits::EMPTY;
-
- for flag in Self::FLAGS.iter() {
- let flag = flag.value();
-
- if bits & flag.bits() == flag.bits() {
- truncated = truncated | flag.bits();
- }
- }
-
- Self::from_bits_retain(truncated)
+ Self::from_bits_retain(bits & Self::all().bits())
}
- /// Convert from underlying bit representation, preserving all
- /// bits (even those not corresponding to a defined flag).
+ /// Convert from a bits value exactly.
fn from_bits_retain(bits: Self::Bits) -> Self;
- /// Get the flag for a particular name.
+ /// Get a flags value with the bits of a flag with the given name set.
+ ///
+ /// This method will return `None` if `name` is empty or doesn't
+ /// correspond to any named flag.
fn from_name(name: &str) -> Option<Self> {
+ // Don't parse empty names as empty flags
+ if name.is_empty() {
+ return None;
+ }
+
for flag in Self::FLAGS {
if flag.name() == name {
- return Some(Self::from_bits_retain(flag.value().bits()))
+ return Some(Self::from_bits_retain(flag.value().bits()));
}
}
None
}
- /// Iterate over enabled flag values.
+ /// Yield a set of contained flags values.
+ ///
+ /// Each yielded flags value will correspond to a defined named flag. Any unknown bits
+ /// will be yielded together as a final flags value.
fn iter(&self) -> iter::Iter<Self> {
iter::Iter::new(self)
}
- /// Iterate over the raw names and bits for enabled flag values.
+ /// Yield a set of contained named flags values.
+ ///
+ /// This method is like [`Flags::iter`], except only yields bits in contained named flags.
+ /// Any unknown bits, or bits not corresponding to a contained flag will not be yielded.
fn iter_names(&self) -> iter::IterNames<Self> {
iter::IterNames::new(self)
}
- /// Returns `true` if no flags are currently stored.
+ /// Whether all bits in this flags value are unset.
fn is_empty(&self) -> bool {
self.bits() == Self::Bits::EMPTY
}
- /// Returns `true` if all flags are currently set.
+ /// Whether all known bits in this flags value are set.
fn is_all(&self) -> bool {
// NOTE: We check against `Self::all` here, not `Self::Bits::ALL`
// because the set of all flags may not use all bits
Self::all().bits() | self.bits() == self.bits()
}
- /// Returns `true` if there are flags common to both `self` and `other`.
+ /// Whether any set bits in a source flags value are also set in a target flags value.
fn intersects(&self, other: Self) -> bool
where
Self: Sized,
@@ -134,7 +232,7 @@ pub trait Flags: Sized + 'static {
self.bits() & other.bits() != Self::Bits::EMPTY
}
- /// Returns `true` if all of the flags in `other` are contained within `self`.
+ /// Whether all set bits in a source flags value are also set in a target flags value.
fn contains(&self, other: Self) -> bool
where
Self: Sized,
@@ -142,31 +240,34 @@ pub trait Flags: Sized + 'static {
self.bits() & other.bits() == other.bits()
}
- /// Inserts the specified flags in-place.
+ /// The bitwise or (`|`) of the bits in two flags values.
fn insert(&mut self, other: Self)
where
Self: Sized,
{
- *self = Self::from_bits_retain(self.bits() | other.bits());
+ *self = Self::from_bits_retain(self.bits()).union(other);
}
- /// Removes the specified flags in-place.
+ /// The intersection of a source flags value with the complement of a target flags value (`&!`).
+ ///
+ /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
+ /// `remove` won't truncate `other`, but the `!` operator will.
fn remove(&mut self, other: Self)
where
Self: Sized,
{
- *self = Self::from_bits_retain(self.bits() & !other.bits());
+ *self = Self::from_bits_retain(self.bits()).difference(other);
}
- /// Toggles the specified flags in-place.
+ /// The bitwise exclusive-or (`^`) of the bits in two flags values.
fn toggle(&mut self, other: Self)
where
Self: Sized,
{
- *self = Self::from_bits_retain(self.bits() ^ other.bits());
+ *self = Self::from_bits_retain(self.bits()).symmetric_difference(other);
}
- /// Inserts or removes the specified flags depending on the passed value.
+ /// Call [`Flags::insert`] when `value` is `true` or [`Flags::remove`] when `value` is `false`.
fn set(&mut self, other: Self, value: bool)
where
Self: Sized,
@@ -178,64 +279,43 @@ pub trait Flags: Sized + 'static {
}
}
- /// Returns the intersection between the flags in `self` and
- /// `other`.
- ///
- /// Specifically, the returned set contains only the flags which are
- /// present in *both* `self` *and* `other`.
+ /// The bitwise and (`&`) of the bits in two flags values.
#[must_use]
fn intersection(self, other: Self) -> Self {
Self::from_bits_retain(self.bits() & other.bits())
}
- /// Returns the union of between the flags in `self` and `other`.
- ///
- /// Specifically, the returned set contains all flags which are
- /// present in *either* `self` *or* `other`, including any which are
- /// present in both (see [`Self::symmetric_difference`] if that
- /// is undesirable).
+ /// The bitwise or (`|`) of the bits in two flags values.
#[must_use]
fn union(self, other: Self) -> Self {
Self::from_bits_retain(self.bits() | other.bits())
}
- /// Returns the difference between the flags in `self` and `other`.
- ///
- /// Specifically, the returned set contains all flags present in
- /// `self`, except for the ones present in `other`.
+ /// The intersection of a source flags value with the complement of a target flags value (`&!`).
///
- /// It is also conceptually equivalent to the "bit-clear" operation:
- /// `flags & !other` (and this syntax is also supported).
+ /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
+ /// `difference` won't truncate `other`, but the `!` operator will.
#[must_use]
fn difference(self, other: Self) -> Self {
Self::from_bits_retain(self.bits() & !other.bits())
}
- /// Returns the [symmetric difference][sym-diff] between the flags
- /// in `self` and `other`.
- ///
- /// Specifically, the returned set contains the flags present which
- /// are present in `self` or `other`, but that are not present in
- /// both. Equivalently, it contains the flags present in *exactly
- /// one* of the sets `self` and `other`.
- ///
- /// [sym-diff]: https://en.wikipedia.org/wiki/Symmetric_difference
+ /// The bitwise exclusive-or (`^`) of the bits in two flags values.
#[must_use]
fn symmetric_difference(self, other: Self) -> Self {
Self::from_bits_retain(self.bits() ^ other.bits())
}
- /// Returns the complement of this set of flags.
- ///
- /// Specifically, the returned set contains all the flags which are
- /// not set in `self`, but which are allowed for this type.
+ /// The bitwise negation (`!`) of the bits in a flags value, truncating the result.
#[must_use]
fn complement(self) -> Self {
Self::from_bits_truncate(!self.bits())
}
}
-/// Underlying storage for a flags type.
+/**
+A bits type that can be used as storage for a flags type.
+*/
pub trait Bits:
Clone
+ Copy
@@ -247,10 +327,10 @@ pub trait Bits:
+ Sized
+ 'static
{
- /// The value of `Self` where no bits are set.
+ /// A value with all bits unset.
const EMPTY: Self;
- /// The value of `Self` where all bits are set.
+ /// A value with all bits set.
const ALL: Self;
}
@@ -320,6 +400,7 @@ pub trait PublicFlags {
type Internal;
}
+#[doc(hidden)]
#[deprecated(note = "use the `Flags` trait instead")]
pub trait BitFlags: ImplementedByBitFlagsMacro + Flags {
/// An iterator over enabled flags in an instance of the type.