I am implementing an adapter to an existing (web) API, where selected weekdays for home automation routines are represented as a string of a bit mask of length 7 starting at left from Monday until Sunday (e.g. "1001010"
translates to Monday, Thursday and Saturday). To use the weekdays more ergonomically in my codebase, I wrote a parser module for it:
use chrono::Weekday;
use std::collections::HashSet;
use std::fmt::{Display, Formatter};
use std::hash::BuildHasher;
use std::num::ParseIntError;
use std::ops::{BitAnd, BitOr};
use std::str::FromStr;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WeekdayMask(u8);
impl BitAnd for WeekdayMask {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self(self.0 & rhs.0)
}
}
impl BitOr for WeekdayMask {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl Display for WeekdayMask {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:07b}", self.0)
}
}
impl From<u8> for WeekdayMask {
fn from(mask: u8) -> Self {
Self(mask)
}
}
impl From<WeekdayMask> for u8 {
fn from(mask: WeekdayMask) -> Self {
mask.0
}
}
impl From<Weekday> for WeekdayMask {
fn from(weekday: Weekday) -> Self {
Self(1 << Weekday::Sun.days_since(weekday))
}
}
impl From<HashSet<Weekday>> for WeekdayMask {
fn from(weekdays: HashSet<Weekday>) -> Self {
weekdays
.iter()
.fold(Self::default(), |mask, weekday| mask | (*weekday).into())
}
}
impl From<&[Weekday]> for WeekdayMask {
fn from(weekdays: &[Weekday]) -> Self {
weekdays
.iter()
.fold(Self::default(), |mask, weekday| mask | (*weekday).into())
}
}
impl<H> From<WeekdayMask> for HashSet<Weekday, H>
where
H: Default + BuildHasher,
{
fn from(mask: WeekdayMask) -> Self {
Weekday::Mon
.iter_week()
.filter(|&weekday| (mask & weekday.into()) != WeekdayMask::default())
.collect()
}
}
impl FromStr for WeekdayMask {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
u8::from_str_radix(s, 2).map(Self)
}
}
pub trait IterWeek {
fn iter_week(self) -> WeekIterator;
}
impl IterWeek for Weekday {
fn iter_week(self) -> WeekIterator {
WeekIterator::new(self)
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct WeekIterator {
start: Weekday,
next: Option<Weekday>,
}
impl WeekIterator {
#[must_use]
pub const fn new(start: Weekday) -> Self {
Self { start, next: None }
}
}
impl Iterator for WeekIterator {
type Item = Weekday;
fn next(&mut self) -> Option<Self::Item> {
if let Some(next) = self.next {
if next == self.start {
None
} else {
self.next.replace(next.succ());
Some(next)
}
} else {
self.next.replace(self.start.succ());
Some(self.start)
}
}
}
What can I improve? Did I reinvent the wheel somewhere?