use crate::func::args::ArgCountOutOfBoundsError;
use core::fmt::{Debug, Formatter};
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ArgCount {
bits: u64,
len: u8,
}
impl ArgCount {
pub const MAX_COUNT: usize = u64::BITS as usize - 1;
pub fn new(count: usize) -> Result<Self, ArgCountOutOfBoundsError> {
Ok(Self {
bits: 1 << Self::try_to_u8(count)?,
len: 1,
})
}
pub fn add(&mut self, count: usize) {
self.try_add(count).unwrap();
}
pub fn try_add(&mut self, count: usize) -> Result<(), ArgCountOutOfBoundsError> {
let count = Self::try_to_u8(count)?;
if !self.contains_unchecked(count) {
self.len += 1;
self.bits |= 1 << count;
}
Ok(())
}
pub fn remove(&mut self, count: usize) {
self.try_remove(count).unwrap();
}
pub fn try_remove(&mut self, count: usize) -> Result<(), ArgCountOutOfBoundsError> {
let count = Self::try_to_u8(count)?;
if self.contains_unchecked(count) {
self.len -= 1;
self.bits &= !(1 << count);
}
Ok(())
}
pub fn contains(&self, count: usize) -> bool {
count < usize::BITS as usize && (self.bits >> count) & 1 == 1
}
pub fn len(&self) -> usize {
self.len as usize
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn iter(&self) -> ArgCountIter {
ArgCountIter {
count: *self,
index: 0,
found: 0,
}
}
fn contains_unchecked(&self, count: u8) -> bool {
(self.bits >> count) & 1 == 1
}
fn try_to_u8(count: usize) -> Result<u8, ArgCountOutOfBoundsError> {
if count > Self::MAX_COUNT {
Err(ArgCountOutOfBoundsError(count))
} else {
Ok(count as u8)
}
}
}
impl Default for ArgCount {
fn default() -> Self {
Self { bits: 0, len: 0 }
}
}
impl Debug for ArgCount {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_set().entries(self.iter()).finish()
}
}
pub struct ArgCountIter {
count: ArgCount,
index: u8,
found: u8,
}
impl Iterator for ArgCountIter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.index as usize > ArgCount::MAX_COUNT {
return None;
}
if self.found == self.count.len {
return None;
}
if self.count.contains_unchecked(self.index) {
self.index += 1;
self.found += 1;
return Some(self.index as usize - 1);
}
self.index += 1;
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.count.len(), Some(self.count.len()))
}
}
impl ExactSizeIterator for ArgCountIter {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_default_to_empty() {
let count = ArgCount::default();
assert_eq!(count.len(), 0);
assert!(count.is_empty());
assert!(!count.contains(0));
}
#[test]
fn should_construct_with_count() {
let count = ArgCount::new(3).unwrap();
assert_eq!(count.len(), 1);
assert!(!count.is_empty());
assert!(count.contains(3));
}
#[test]
fn should_add_count() {
let mut count = ArgCount::default();
count.add(3);
assert_eq!(count.len(), 1);
assert!(count.contains(3));
}
#[test]
fn should_add_multiple_counts() {
let mut count = ArgCount::default();
count.add(3);
count.add(5);
count.add(7);
assert_eq!(count.len(), 3);
assert!(!count.contains(0));
assert!(!count.contains(1));
assert!(!count.contains(2));
assert!(count.contains(3));
assert!(count.contains(5));
assert!(count.contains(7));
}
#[test]
fn should_add_idempotently() {
let mut count = ArgCount::default();
count.add(3);
count.add(3);
assert_eq!(count.len(), 1);
assert!(count.contains(3));
}
#[test]
fn should_remove_count() {
let mut count = ArgCount::default();
count.add(3);
assert_eq!(count.len(), 1);
assert!(count.contains(3));
count.remove(3);
assert_eq!(count.len(), 0);
assert!(!count.contains(3));
}
#[test]
fn should_allow_removing_nonexistent_count() {
let mut count = ArgCount::default();
assert_eq!(count.len(), 0);
assert!(!count.contains(3));
count.remove(3);
assert_eq!(count.len(), 0);
assert!(!count.contains(3));
}
#[test]
fn should_iterate_over_counts() {
let mut count = ArgCount::default();
count.add(3);
count.add(5);
count.add(7);
let mut iter = count.iter();
assert_eq!(iter.len(), 3);
assert_eq!(iter.next(), Some(3));
assert_eq!(iter.next(), Some(5));
assert_eq!(iter.next(), Some(7));
assert_eq!(iter.next(), None);
}
#[test]
fn should_return_error_for_out_of_bounds_count() {
let count = ArgCount::new(64);
assert_eq!(count, Err(ArgCountOutOfBoundsError(64)));
let mut count = ArgCount::default();
assert_eq!(count.try_add(64), Err(ArgCountOutOfBoundsError(64)));
assert_eq!(count.try_remove(64), Err(ArgCountOutOfBoundsError(64)));
}
#[test]
fn should_return_false_for_out_of_bounds_contains() {
let count = ArgCount::default();
assert!(!count.contains(64));
}
}