#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[repr(transparent)]
pub struct FieldMask(pub u64);
impl FieldMask {
#[inline]
#[must_use]
pub const fn empty() -> Self {
Self(0)
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0 == 0
}
#[inline]
#[must_use]
pub const fn with_bit(self, bit: FieldBit) -> Self {
Self(self.0 | (1u64 << bit.get()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[repr(transparent)]
pub struct CompletedMask(pub u64);
impl CompletedMask {
#[inline]
#[must_use]
pub const fn empty() -> Self {
Self(0)
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0 == 0
}
#[inline]
#[must_use]
pub const fn with_bit(self, bit: FieldBit) -> Self {
Self(self.0 | (1u64 << bit.get()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[repr(transparent)]
pub struct FieldBit(u8);
impl FieldBit {
#[inline]
pub const fn new_checked(value: u8) -> Result<Self, MaskError> {
if value < 64 {
Ok(Self(value))
} else {
Err(MaskError::OutOfRange)
}
}
#[inline]
#[must_use]
pub const fn new_unchecked(value: u8) -> Self {
Self(value)
}
#[inline]
#[must_use]
pub const fn get(self) -> u8 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MaskError {
OutOfRange,
}
impl core::fmt::Display for MaskError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::OutOfRange => write!(f, "FieldBit must be in range [0, 63]"),
}
}
}
impl std::error::Error for MaskError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn with_bit_matches_pow2_for_every_bit() {
for b in 0u8..64u8 {
let bit = FieldBit::new_checked(b).unwrap();
let m = FieldMask::empty().with_bit(bit);
assert_eq!(m.0, 1u64 << b, "FieldMask bit {b} mismatch");
let cm = CompletedMask::empty().with_bit(bit);
assert_eq!(cm.0, 1u64 << b, "CompletedMask bit {b} mismatch");
}
}
#[test]
fn field_bit_range_contract() {
for v in 0u8..64u8 {
assert_eq!(FieldBit::new_checked(v).unwrap().get(), v);
}
for v in 64u8..=200u8 {
assert_eq!(FieldBit::new_checked(v), Err(MaskError::OutOfRange));
}
}
}