diff options
Diffstat (limited to 'src')
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 } } @@ -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. |