Weekdays parsing from bitmask, represented as string

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?

bitflags

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.