diff options
Diffstat (limited to 'src/traits.rs')
-rw-r--r-- | src/traits.rs | 269 |
1 files changed, 175 insertions, 94 deletions
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. |