use core::any::type_name;
use core::borrow::Borrow;
use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::iter::{FromIterator, IntoIterator};
use core::marker::PhantomData;
use core::ops::{
BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, Sub, SubAssign,
};
use core::str::FromStr;
use crate::{BitFlag, BitFlagsIter, BitFlagsSpec, NameMatcher, util};
#[repr(transparent)]
pub struct BitFlags<T, U>(
T,
) -> *const U> is Send + Sync and covariant over `U`,
PhantomData<fn() -> *const U>,
)
where
U: ?Sized;
#[cfg(feature = "serde")]
struct BitFlagsVisitor<T, U>(PhantomData<T>, PhantomData<fn() -> *const U>)
where
U: ?Sized;
impl<T, U> BitFlags<T, U>
where
U: ?Sized,
{
#[inline]
#[must_use]
pub const fn empty() -> Self
where
T: util::IntOpsExt,
{
Self(T::ZERO, PhantomData)
}
#[doc(alias = "all")]
#[inline]
#[must_use]
pub const fn all_valid() -> Self
where
T: 'static,
U: BitFlagsSpec<T>,
{
Self(U::VALID_BITS_MASK, PhantomData)
}
#[inline]
#[must_use]
pub const fn from_bits_retain(bits: T) -> Self {
Self(bits, PhantomData)
}
#[inline]
#[must_use]
pub const fn bits(&self) -> T
where
T: Copy,
{
self.0
}
#[inline]
#[must_use]
pub const fn bits_mut(&mut self) -> &mut T {
&mut self.0
}
#[inline]
#[must_use]
pub fn into_bits(self) -> T {
self.0
}
#[inline]
pub const fn iter(self) -> BitFlagsIter<T, U>
where
T: Copy + Not<Output = T> + BitAndAssign<T> + util::IntOpsExt,
{
BitFlagsIter::new(self)
}
#[inline]
pub fn try_from_str(
input: &str,
separator: &'static str,
name_matcher: NameMatcher,
) -> Result<Self, BitFlagsParseError>
where
T: 'static + Copy + BitOrAssign<T> + util::IntOpsExt,
U: BitFlagsSpec<T>,
{
if input.trim().is_empty() {
return Ok(Self::empty());
}
let mut this = Self::empty();
let input = input.split(separator);
'a: for input in input {
let input = input.trim();
let flag = BitFlag::from_name_opt(input, name_matcher);
if let Some(flag) = flag {
this |= flag;
continue;
}
for (flags, name) in U::COMBINED_NAMES {
if input == *name {
this |= Self::from_bits_retain(*flags);
continue 'a;
}
}
if let Some(maybe_hex_flag) = input.strip_prefix("0x") {
if !maybe_hex_flag.starts_with('+') {
let bits = T::from_str_radix(maybe_hex_flag, 16);
if let Ok(bits) = bits {
this |= Self::from_bits_retain(bits);
continue;
}
}
}
#[cfg(feature = "alloc")]
let given = alloc::string::ToString::to_string(input);
#[cfg(not(feature = "alloc"))]
let given = ();
let expected = U::FLAG_NAMES;
return Err(BitFlagsParseError {
given,
separator,
expected,
});
}
Ok(this)
}
}
macro_rules! impl_bit_flags {
($ty:ty) => {
impl<U> BitFlags<$ty, U>
where
U: ?Sized,
{
#[inline]
#[must_use]
pub const fn from_flag(flag: BitFlag<$ty, U>) -> Self {
Self::from_bit_retain(flag.bit())
}
#[inline]
#[must_use]
pub const fn from_bit_retain(bit: u8) -> Self {
#[expect(clippy::as_conversions, reason = "supported in const functions")]
let bit = bit as u32;
if bit < <$ty>::BITS {
Self::from_bits_retain(1 << bit)
} else {
Self::empty()
}
}
#[inline]
#[must_use]
pub const fn from_bit_truncate(bit: u8) -> Self
where
U: BitFlagsSpec<$ty>,
{
#[expect(clippy::as_conversions, reason = "supported in const functions")]
let bit = bit as u32;
if bit < <$ty>::BITS {
Self::from_bits_truncate(1 << bit)
} else {
Self::empty()
}
}
#[inline]
#[must_use]
pub const fn try_from_bit(bit: u8) -> Option<Self>
where
U: BitFlagsSpec<$ty>,
{
#[expect(clippy::as_conversions, reason = "supported in const functions")]
let bit = bit as u32;
if bit < <$ty>::BITS {
Self::try_from_bits(1 << bit)
} else {
None
}
}
#[doc(alias = "from_bits")]
#[inline]
#[must_use]
pub const fn try_from_bits(bits: $ty) -> Option<Self>
where
U: BitFlagsSpec<$ty>,
{
if bits & !U::VALID_BITS_MASK == 0 {
Some(Self(bits, PhantomData))
} else {
None
}
}
#[inline]
#[must_use]
pub const fn from_bits_truncate(bits: $ty) -> Self
where
U: BitFlagsSpec<$ty>,
{
Self(bits & U::VALID_BITS_MASK, PhantomData)
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0 == 0
}
#[doc(alias = "is_all")]
#[inline]
#[must_use]
pub const fn is_valid(&self) -> bool
where
U: BitFlagsSpec<$ty>,
{
self.bits() & !U::VALID_BITS_MASK == 0
}
#[inline]
#[must_use]
pub const fn intersects(&self, other: Self) -> bool {
(self.0 & other.0) != 0
}
#[inline]
#[must_use]
pub const fn contains(&self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
#[inline]
pub const fn insert(&mut self, other: Self) {
self.0 |= other.0;
}
#[inline]
pub const fn clear(&mut self) {
self.0 = 0;
}
#[inline]
pub const fn truncate(&mut self)
where
U: BitFlagsSpec<$ty>,
{
self.0 &= U::VALID_BITS_MASK
}
#[inline]
pub const fn remove(&mut self, other: Self) {
self.0 &= !other.0;
}
#[inline]
pub const fn toggle(&mut self, other: Self) {
self.0 ^= other.0;
}
#[inline]
pub const fn set(&mut self, other: Self, value: bool) {
if value {
self.insert(other)
} else {
self.remove(other)
}
}
#[inline]
#[must_use]
pub const fn intersection(self, other: Self) -> Self {
Self(self.0 & other.0, PhantomData)
}
#[inline]
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0, PhantomData)
}
#[inline]
#[must_use]
pub const fn difference(self, other: Self) -> Self {
Self(self.0 & !other.0, PhantomData)
}
#[inline]
#[must_use]
pub const fn symmetric_difference(self, other: Self) -> Self {
Self(self.0 ^ other.0, PhantomData)
}
#[inline]
#[must_use]
pub const fn complement(self) -> Self
where
U: BitFlagsSpec<$ty>,
{
Self(!self.0 & U::VALID_BITS_MASK, PhantomData)
}
}
impl<U> From<BitFlags<$ty, U>> for $ty
where
U: ?Sized,
{
#[inline]
fn from(flags: BitFlags<$ty, U>) -> Self {
flags.0
}
}
};
}
impl_bit_flags!(u8);
impl_bit_flags!(u16);
impl_bit_flags!(u32);
impl_bit_flags!(u64);
impl_bit_flags!(u128);
impl_bit_flags!(usize);
impl_bit_flags!(i8);
impl_bit_flags!(i16);
impl_bit_flags!(i32);
impl_bit_flags!(i64);
impl_bit_flags!(i128);
impl_bit_flags!(isize);
impl<T, U> Copy for BitFlags<T, U>
where
T: Copy,
U: ?Sized,
{
}
impl<T, U> Clone for BitFlags<T, U>
where
T: Clone,
U: ?Sized,
{
#[inline]
fn clone(&self) -> Self {
Self(self.0.clone(), PhantomData)
}
}
impl<T, U> Eq for BitFlags<T, U>
where
T: Eq,
U: ?Sized,
{
}
impl<T, U> PartialEq for BitFlags<T, U>
where
T: PartialEq,
U: ?Sized,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T, U> PartialEq<BitFlag<T, U>> for BitFlags<T, U>
where
T: Copy + PartialEq + util::IntOpsExt,
U: ?Sized,
{
#[inline]
fn eq(&self, other: &BitFlag<T, U>) -> bool {
self.eq(&Self::from(*other))
}
}
impl<T, U> PartialEq<BitFlags<T, U>> for BitFlag<T, U>
where
T: Copy + PartialEq + util::IntOpsExt,
U: ?Sized,
{
#[inline]
fn eq(&self, other: &BitFlags<T, U>) -> bool {
BitFlags::from(*self).eq(other)
}
}
impl<T, U> Ord for BitFlags<T, U>
where
T: Ord,
U: ?Sized,
{
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl<T, U> PartialOrd for BitFlags<T, U>
where
T: PartialOrd,
U: ?Sized,
{
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T, U> PartialOrd<BitFlag<T, U>> for BitFlags<T, U>
where
T: Copy + PartialOrd + util::IntOpsExt,
U: ?Sized,
{
#[inline]
fn partial_cmp(&self, other: &BitFlag<T, U>) -> Option<Ordering> {
self.partial_cmp(&Self::from(*other))
}
}
impl<T, U> PartialOrd<BitFlags<T, U>> for BitFlag<T, U>
where
T: Copy + PartialOrd + util::IntOpsExt,
U: ?Sized,
{
#[inline]
fn partial_cmp(&self, other: &BitFlags<T, U>) -> Option<Ordering> {
BitFlags::from(*self).partial_cmp(other)
}
}
impl<T, U> Hash for BitFlags<T, U>
where
T: Hash,
U: ?Sized,
{
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T, U> Default for BitFlags<T, U>
where
T: Default,
U: ?Sized,
{
#[inline]
fn default() -> Self {
Self(T::default(), PhantomData)
}
}
impl<T, U> From<BitFlag<T, U>> for BitFlags<T, U>
where
T: util::IntOpsExt,
U: ?Sized,
{
#[inline]
fn from(flag: BitFlag<T, U>) -> Self {
let bit = flag.bit();
T::ONE
.checked_shl(u32::from(bit))
.map_or_else(Self::empty, Self::from_bits_retain)
}
}
impl<T, U> AsRef<T> for BitFlags<T, U>
where
U: ?Sized,
{
#[inline]
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T, U> Borrow<T> for BitFlags<T, U>
where
U: ?Sized,
{
#[inline]
fn borrow(&self) -> &T {
&self.0
}
}
impl<T, U> BitAnd<Self> for BitFlags<T, U>
where
T: BitAnd<T, Output = T>,
U: ?Sized,
{
type Output = Self;
#[inline]
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0, PhantomData)
}
}
impl<T, U> BitAnd<BitFlag<T, U>> for BitFlags<T, U>
where
T: BitAnd<T, Output = T> + util::IntOpsExt,
U: ?Sized,
{
type Output = Self;
#[inline]
fn bitand(self, rhs: BitFlag<T, U>) -> Self::Output {
self.bitand(Self::from(rhs))
}
}
impl<T, U> BitAnd<BitFlags<T, U>> for BitFlag<T, U>
where
T: BitAnd<T, Output = T> + util::IntOpsExt,
U: ?Sized,
{
type Output = BitFlags<T, U>;
#[inline]
fn bitand(self, rhs: BitFlags<T, U>) -> Self::Output {
BitFlags::from(self).bitand(rhs)
}
}
impl<T, U> BitAndAssign<Self> for BitFlags<T, U>
where
T: BitAndAssign<T>,
U: ?Sized,
{
#[inline]
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl<T, U> BitAndAssign<BitFlag<T, U>> for BitFlags<T, U>
where
T: BitAndAssign<T> + util::IntOpsExt,
U: ?Sized,
{
#[inline]
fn bitand_assign(&mut self, rhs: BitFlag<T, U>) {
self.bitand_assign(Self::from(rhs));
}
}
impl<T, U> BitOr<Self> for BitFlags<T, U>
where
T: BitOr<T, Output = T>,
U: ?Sized,
{
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0, PhantomData)
}
}
impl<T, U> BitOr<BitFlag<T, U>> for BitFlags<T, U>
where
T: BitOr<T, Output = T> + util::IntOpsExt,
U: ?Sized,
{
type Output = Self;
#[inline]
fn bitor(self, rhs: BitFlag<T, U>) -> Self::Output {
self.bitor(Self::from(rhs))
}
}
impl<T, U> BitOr<BitFlags<T, U>> for BitFlag<T, U>
where
T: BitOr<T, Output = T> + util::IntOpsExt,
U: ?Sized,
{
type Output = BitFlags<T, U>;
#[inline]
fn bitor(self, rhs: BitFlags<T, U>) -> Self::Output {
BitFlags::from(self).bitor(rhs)
}
}
impl<T, U> BitOrAssign<Self> for BitFlags<T, U>
where
T: BitOrAssign<T>,
U: ?Sized,
{
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl<T, U> BitOrAssign<BitFlag<T, U>> for BitFlags<T, U>
where
T: BitOrAssign<T> + util::IntOpsExt,
U: ?Sized,
{
#[inline]
fn bitor_assign(&mut self, rhs: BitFlag<T, U>) {
self.bitor_assign(Self::from(rhs));
}
}
impl<T, U> BitXor<Self> for BitFlags<T, U>
where
T: BitXor<T, Output = T>,
U: ?Sized,
{
type Output = Self;
#[inline]
fn bitxor(self, rhs: Self) -> Self::Output {
Self(self.0 ^ rhs.0, PhantomData)
}
}
impl<T, U> BitXor<BitFlag<T, U>> for BitFlags<T, U>
where
T: BitXor<T, Output = T> + util::IntOpsExt,
U: ?Sized,
{
type Output = Self;
#[inline]
fn bitxor(self, rhs: BitFlag<T, U>) -> Self::Output {
self.bitxor(Self::from(rhs))
}
}
impl<T, U> BitXor<BitFlags<T, U>> for BitFlag<T, U>
where
T: BitXor<T, Output = T> + util::IntOpsExt,
U: ?Sized,
{
type Output = BitFlags<T, U>;
#[inline]
fn bitxor(self, rhs: BitFlags<T, U>) -> Self::Output {
BitFlags::from(self).bitxor(rhs)
}
}
impl<T, U> BitXorAssign<Self> for BitFlags<T, U>
where
T: BitXorAssign<T>,
U: ?Sized,
{
#[inline]
fn bitxor_assign(&mut self, rhs: Self) {
self.0 ^= rhs.0;
}
}
impl<T, U> BitXorAssign<BitFlag<T, U>> for BitFlags<T, U>
where
T: BitXorAssign<T> + util::IntOpsExt,
U: ?Sized,
{
#[inline]
fn bitxor_assign(&mut self, rhs: BitFlag<T, U>) {
self.bitxor_assign(Self::from(rhs));
}
}
impl<T, U> Sub<Self> for BitFlags<T, U>
where
T: Not<Output = T> + BitAnd<T, Output = T>,
U: ?Sized,
{
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 & !rhs.0, PhantomData)
}
}
impl<T, U> Sub<BitFlag<T, U>> for BitFlags<T, U>
where
T: Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
U: ?Sized,
{
type Output = Self;
#[inline]
fn sub(self, rhs: BitFlag<T, U>) -> Self::Output {
self.sub(Self::from(rhs))
}
}
impl<T, U> Sub<BitFlags<T, U>> for BitFlag<T, U>
where
T: Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
U: ?Sized,
{
type Output = BitFlags<T, U>;
#[inline]
fn sub(self, rhs: BitFlags<T, U>) -> Self::Output {
BitFlags::from(self).sub(rhs)
}
}
impl<T, U> SubAssign<Self> for BitFlags<T, U>
where
T: Not<Output = T> + BitAndAssign<T>,
U: ?Sized,
{
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.0 &= !rhs.0;
}
}
impl<T, U> SubAssign<BitFlag<T, U>> for BitFlags<T, U>
where
T: Not<Output = T> + BitAndAssign<T> + util::IntOpsExt,
U: ?Sized,
{
#[inline]
fn sub_assign(&mut self, rhs: BitFlag<T, U>) {
self.sub_assign(Self::from(rhs));
}
}
impl<T, U> Not for BitFlags<T, U>
where
T: 'static + Not<Output = T> + BitAnd<T, Output = T>,
U: ?Sized + BitFlagsSpec<T>,
{
type Output = Self;
#[inline]
fn not(self) -> Self::Output {
Self(!self.0 & U::VALID_BITS_MASK, PhantomData)
}
}
impl<T, U> fmt::Display for BitFlags<T, U>
where
T: 'static
+ Copy
+ PartialEq
+ Not<Output = T>
+ BitAnd<T, Output = T>
+ Shl<usize, Output = T>
+ fmt::LowerHex
+ util::IntOpsExt,
U: ?Sized + BitFlagsSpec<T>,
{
#[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut separator = "";
let mut this = self.0;
for (i, names) in U::FLAG_NAMES.iter().enumerate() {
let Some(i) = u32::try_from(i).ok() else {
break; };
let Some(mask) = T::ONE.checked_shl(i) else {
break;
};
if let Some(name) = names.first() {
let new_this = this & !mask;
if this != new_this {
write!(f, "{separator}{name}")?;
separator = " | ";
}
this = new_this;
}
}
if this != T::ZERO {
write!(f, "{separator}{this:#x}")?;
}
Ok(())
}
}
impl<T, U> fmt::Debug for BitFlags<T, U>
where
T: fmt::LowerHex,
U: ?Sized,
{
#[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"BitFlags({:#x}, PhantomData<{}>)",
self.0,
type_name::<U>()
)
}
}
impl<T, U> fmt::Binary for BitFlags<T, U>
where
T: fmt::Binary,
U: ?Sized,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Binary::fmt(&self.0, f)
}
}
impl<T, U> fmt::Octal for BitFlags<T, U>
where
T: fmt::Octal,
U: ?Sized,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Octal::fmt(&self.0, f)
}
}
impl<T, U> fmt::LowerHex for BitFlags<T, U>
where
T: fmt::LowerHex,
U: ?Sized,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(&self.0, f)
}
}
impl<T, U> fmt::UpperHex for BitFlags<T, U>
where
T: fmt::UpperHex,
U: ?Sized,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::UpperHex::fmt(&self.0, f)
}
}
impl<T, U> FromStr for BitFlags<T, U>
where
T: 'static + Copy + BitOrAssign<T> + util::IntOpsExt,
U: ?Sized + BitFlagsSpec<T>,
{
type Err = BitFlagsParseError;
#[inline]
fn from_str(input: &str) -> Result<Self, Self::Err> {
Self::try_from_str(input, "|", U::DEFAULT_NAME_MATCHER)
}
}
impl<T, U> IntoIterator for BitFlags<T, U>
where
T: Copy + Not<Output = T> + BitAndAssign<T> + util::IntOpsExt,
U: ?Sized,
{
type IntoIter = BitFlagsIter<T, U>;
type Item = BitFlag<T, U>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
BitFlagsIter::new(self)
}
}
impl<T, U> IntoIterator for &BitFlags<T, U>
where
T: Copy + Not<Output = T> + BitAndAssign<T> + util::IntOpsExt,
U: ?Sized,
{
type IntoIter = BitFlagsIter<T, U>;
type Item = BitFlag<T, U>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
BitFlagsIter::new(*self)
}
}
#[expect(
clippy::into_iter_without_iter,
reason = "`iter` and `iter_mut` are the same"
)]
impl<T, U> IntoIterator for &mut BitFlags<T, U>
where
T: Copy + Not<Output = T> + BitAndAssign<T> + util::IntOpsExt,
U: ?Sized,
{
type IntoIter = BitFlagsIter<T, U>;
type Item = BitFlag<T, U>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
BitFlagsIter::new(*self)
}
}
impl<T, U> Extend<Self> for BitFlags<T, U>
where
T: BitOrAssign<T>,
U: ?Sized,
{
#[inline]
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = Self>,
{
for other in iter {
self.0 |= other.0;
}
}
}
impl<T, U> Extend<BitFlag<T, U>> for BitFlags<T, U>
where
T: BitOrAssign<T> + util::IntOpsExt,
U: ?Sized,
{
#[inline]
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = BitFlag<T, U>>,
{
for other in iter {
*self |= Self::from(other);
}
}
}
impl<T, U> FromIterator<Self> for BitFlags<T, U>
where
T: BitOrAssign<T> + util::IntOpsExt,
U: ?Sized,
{
#[inline]
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = Self>,
{
let mut this = Self(T::ZERO, PhantomData);
for other in iter {
this.0 |= other.0;
}
this
}
}
impl<T, U> FromIterator<BitFlag<T, U>> for BitFlags<T, U>
where
T: BitOrAssign<T> + util::IntOpsExt,
U: ?Sized,
{
#[inline]
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = BitFlag<T, U>>,
{
let mut this = Self(T::ZERO, PhantomData);
for other in iter {
this |= Self::from(other);
}
this
}
}
#[cfg(feature = "arbitrary")]
#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
impl<'a, T, U> arbitrary::Arbitrary<'a> for BitFlags<T, U>
where
T: arbitrary::Arbitrary<'a>,
U: ?Sized,
{
#[inline]
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
T::arbitrary(u).map(|u| Self(u, PhantomData))
}
#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
T::size_hint(depth)
}
}
#[cfg(feature = "bytemuck")]
#[cfg_attr(docsrs, doc(cfg(feature = "bytemuck")))]
unsafe impl<T, U> bytemuck::Zeroable for BitFlags<T, U>
where
T: bytemuck::Zeroable,
U: ?Sized,
{
}
#[cfg(feature = "bytemuck")]
#[cfg_attr(docsrs, doc(cfg(feature = "bytemuck")))]
unsafe impl<T, U> bytemuck::Pod for BitFlags<T, U>
where
T: bytemuck::Pod,
U: 'static + ?Sized,
{
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<T, U> serde::Serialize for BitFlags<T, U>
where
T: 'static
+ Copy
+ PartialEq
+ Not<Output = T>
+ BitAnd<T, Output = T>
+ Shl<usize, Output = T>
+ fmt::LowerHex
+ util::IntOpsExt,
U: ?Sized + BitFlagsSpec<T>,
{
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_str(&self)
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de, T, U> serde::Deserialize<'de> for BitFlags<T, U>
where
T: 'static + Copy + BitOrAssign<T> + util::IntOpsExt,
U: ?Sized + BitFlagsSpec<T>,
{
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_str(BitFlagsVisitor::<T, U>(PhantomData, PhantomData))
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<T, U> serde::de::Visitor<'_> for BitFlagsVisitor<T, U>
where
T: 'static + Copy + BitOrAssign<T> + util::IntOpsExt,
U: ?Sized + BitFlagsSpec<T>,
{
type Value = BitFlags<T, U>;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_expected(f, "|", U::FLAG_NAMES)
}
#[inline]
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Self::Value::from_str(value).map_err(E::custom)
}
}
#[cfg_attr(
not(feature = "alloc"),
expect(
missing_copy_implementations,
reason = "the payload is feature-dependent"
)
)]
#[derive(Clone, Debug)]
pub struct BitFlagsParseError {
#[cfg(not(feature = "alloc"))]
given: (),
#[cfg(feature = "alloc")]
given: alloc::string::String,
separator: &'static str,
expected: &'static [&'static [&'static str]],
}
impl fmt::Display for BitFlagsParseError {
#[expect(clippy::missing_inline_in_public_items, reason = "bloats binary size")]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let named_flags = self
.expected
.iter()
.filter(|variants| !variants.is_empty())
.count();
if named_flags == 0 {
write!(f, "unrecognized hex flag, ")?;
} else {
write!(f, "unrecognized named flag or hex flag, ")?;
}
#[cfg(feature = "alloc")]
write!(f, "given: \"{}\", expected ", self.given)?;
#[cfg(not(feature = "alloc"))]
write!(f, "expected ")?;
fmt_expected(f, self.separator, self.expected)?;
#[cfg(not(feature = "alloc"))]
let () = self.given;
Ok(())
}
}
impl core::error::Error for BitFlagsParseError {}
#[inline]
fn fmt_expected(
f: &mut fmt::Formatter<'_>,
separator: &'static str,
expected: &'static [&'static [&'static str]],
) -> fmt::Result {
let named_flags = expected
.iter()
.filter(|variants| !variants.is_empty())
.count();
if named_flags == 0 {
write!(f, "a valid hex flag with \"0x\" prefix")?;
} else {
write!(f, "\"\", or a \"{separator}\"-separated set of")?;
let mut separator = " ";
for names in expected {
if let Some(name) = names.first() {
write!(f, "{separator}\"{name}\"")?;
separator = ", ";
}
}
write!(f, ", or a valid hex flag with \"0x\" prefix")?;
}
Ok(())
}