use core::{
fmt::{self, Debug},
iter::FusedIterator,
};
usecrate::Weekday;
/// A collection of [`Weekday`]s stored as a single byte. /// /// This type is `Copy` and provides efficient set-like and slice-like operations. /// Many operations are `const` as well. /// /// Implemented as a bitmask where bits 1-7 correspond to Monday-Sunday. #[derive(Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] pubstruct WeekdaySet(u8); // Invariant: the 8-th bit is always 0.
impl WeekdaySet { /// Create a `WeekdaySet` from an array of [`Weekday`]s. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// assert_eq!(WeekdaySet::EMPTY, WeekdaySet::from_array([])); /// assert_eq!(WeekdaySet::single(Mon), WeekdaySet::from_array([Mon])); /// assert_eq!(WeekdaySet::ALL, WeekdaySet::from_array([Mon, Tue, Wed, Thu, Fri, Sat, Sun])); /// ``` pubconstfn from_array<const C: usize>(days: [Weekday; C]) -> Self { letmut acc = Self::EMPTY; letmut idx = 0; while idx < days.len() {
acc.0 |= Self::single(days[idx]).0;
idx += 1;
}
acc
}
/// Create a `WeekdaySet` from a single [`Weekday`]. pubconstfn single(weekday: Weekday) -> Self { match weekday {
Weekday::Mon => Self(0b000_0001),
Weekday::Tue => Self(0b000_0010),
Weekday::Wed => Self(0b000_0100),
Weekday::Thu => Self(0b000_1000),
Weekday::Fri => Self(0b001_0000),
Weekday::Sat => Self(0b010_0000),
Weekday::Sun => Self(0b100_0000),
}
}
/// Adds a day to the collection. /// /// Returns `true` if the day was new to the collection. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// let mut weekdays = WeekdaySet::single(Mon); /// assert!(weekdays.insert(Tue)); /// assert!(!weekdays.insert(Tue)); /// ``` pubfn insert(&mutself, day: Weekday) -> bool { ifself.contains(day) { returnfalse;
}
self.0 |= Self::single(day).0; true
}
/// Removes a day from the collection. /// /// Returns `true` if the collection did contain the day. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// let mut weekdays = WeekdaySet::single(Mon); /// assert!(weekdays.remove(Mon)); /// assert!(!weekdays.remove(Mon)); /// ``` pubfn remove(&mutself, day: Weekday) -> bool { ifself.contains(day) { self.0 &= !Self::single(day).0; returntrue;
}
false
}
/// Returns `true` if `other` contains all days in `self`. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// assert!(WeekdaySet::single(Mon).is_subset(WeekdaySet::ALL)); /// assert!(!WeekdaySet::single(Mon).is_subset(WeekdaySet::EMPTY)); /// assert!(WeekdaySet::EMPTY.is_subset(WeekdaySet::single(Mon))); /// ``` pubconstfn is_subset(self, other: Self) -> bool { self.intersection(other).0 == self.0
}
/// Returns days that are in both `self` and `other`. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// assert_eq!(WeekdaySet::single(Mon).intersection(WeekdaySet::single(Mon)), WeekdaySet::single(Mon)); /// assert_eq!(WeekdaySet::single(Mon).intersection(WeekdaySet::single(Tue)), WeekdaySet::EMPTY); /// assert_eq!(WeekdaySet::ALL.intersection(WeekdaySet::single(Mon)), WeekdaySet::single(Mon)); /// assert_eq!(WeekdaySet::ALL.intersection(WeekdaySet::EMPTY), WeekdaySet::EMPTY); /// ``` pubconstfn intersection(self, other: Self) -> Self { Self(self.0 & other.0)
}
/// Returns days that are in either `self` or `other`. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// assert_eq!(WeekdaySet::single(Mon).union(WeekdaySet::single(Mon)), WeekdaySet::single(Mon)); /// assert_eq!(WeekdaySet::single(Mon).union(WeekdaySet::single(Tue)), WeekdaySet::from_array([Mon, Tue])); /// assert_eq!(WeekdaySet::ALL.union(WeekdaySet::single(Mon)), WeekdaySet::ALL); /// assert_eq!(WeekdaySet::ALL.union(WeekdaySet::EMPTY), WeekdaySet::ALL); /// ``` pubconstfn union(self, other: Self) -> Self { Self(self.0 | other.0)
}
/// Returns days that are in `self` or `other` but not in both. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// assert_eq!(WeekdaySet::single(Mon).symmetric_difference(WeekdaySet::single(Mon)), WeekdaySet::EMPTY); /// assert_eq!(WeekdaySet::single(Mon).symmetric_difference(WeekdaySet::single(Tue)), WeekdaySet::from_array([Mon, Tue])); /// assert_eq!( /// WeekdaySet::ALL.symmetric_difference(WeekdaySet::single(Mon)), /// WeekdaySet::from_array([Tue, Wed, Thu, Fri, Sat, Sun]), /// ); /// assert_eq!(WeekdaySet::ALL.symmetric_difference(WeekdaySet::EMPTY), WeekdaySet::ALL); /// ``` pubconstfn symmetric_difference(self, other: Self) -> Self { Self(self.0 ^ other.0)
}
/// Returns days that are in `self` but not in `other`. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// assert_eq!(WeekdaySet::single(Mon).difference(WeekdaySet::single(Mon)), WeekdaySet::EMPTY); /// assert_eq!(WeekdaySet::single(Mon).difference(WeekdaySet::single(Tue)), WeekdaySet::single(Mon)); /// assert_eq!(WeekdaySet::EMPTY.difference(WeekdaySet::single(Mon)), WeekdaySet::EMPTY); /// ``` pubconstfn difference(self, other: Self) -> Self { Self(self.0 & !other.0)
}
/// Get the first day in the collection, starting from Monday. /// /// Returns `None` if the collection is empty. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// assert_eq!(WeekdaySet::single(Mon).first(), Some(Mon)); /// assert_eq!(WeekdaySet::single(Tue).first(), Some(Tue)); /// assert_eq!(WeekdaySet::ALL.first(), Some(Mon)); /// assert_eq!(WeekdaySet::EMPTY.first(), None); /// ``` pubconstfn first(self) -> Option<Weekday> { ifself.is_empty() { return None;
}
// Find the first non-zero bit. let bit = 1 << self.0.trailing_zeros();
Self(bit).single_day()
}
/// Get the last day in the collection, starting from Sunday. /// /// Returns `None` if the collection is empty. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// assert_eq!(WeekdaySet::single(Mon).last(), Some(Mon)); /// assert_eq!(WeekdaySet::single(Sun).last(), Some(Sun)); /// assert_eq!(WeekdaySet::from_array([Mon, Tue]).last(), Some(Tue)); /// assert_eq!(WeekdaySet::EMPTY.last(), None); /// ``` pubfn last(self) -> Option<Weekday> { ifself.is_empty() { return None;
}
// Find the last non-zero bit. let bit = 1 << (7 - self.0.leading_zeros());
Self(bit).single_day()
}
/// Split the collection in two at the given day. /// /// Returns a tuple `(before, after)`. `before` contains all days starting from Monday /// up to but __not__ including `weekday`. `after` contains all days starting from `weekday` /// up to and including Sunday. constfn split_at(self, weekday: Weekday) -> (Self, Self) { let days_after = 0b1000_0000 - Self::single(weekday).0; let days_before = days_after ^ 0b0111_1111;
(Self(self.0 & days_before), Self(self.0 & days_after))
}
/// Iterate over the [`Weekday`]s in the collection starting from a given day. /// /// Wraps around from Sunday to Monday if necessary. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// let weekdays = WeekdaySet::from_array([Mon, Wed, Fri]); /// let mut iter = weekdays.iter(Wed); /// assert_eq!(iter.next(), Some(Wed)); /// assert_eq!(iter.next(), Some(Fri)); /// assert_eq!(iter.next(), Some(Mon)); /// assert_eq!(iter.next(), None); /// ``` pubconstfn iter(self, start: Weekday) -> WeekdaySetIter {
WeekdaySetIter { days: self, start }
}
/// Returns `true` if the collection contains the given day. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// assert!(WeekdaySet::single(Mon).contains(Mon)); /// assert!(WeekdaySet::from_array([Mon, Tue]).contains(Tue)); /// assert!(!WeekdaySet::single(Mon).contains(Tue)); /// ``` pubconstfn contains(self, day: Weekday) -> bool { self.0 & Self::single(day).0 != 0
}
/// Returns `true` if the collection is empty. /// /// # Example /// ``` /// # use chrono::{Weekday, WeekdaySet}; /// assert!(WeekdaySet::EMPTY.is_empty()); /// assert!(!WeekdaySet::single(Weekday::Mon).is_empty()); /// ``` pubconstfn is_empty(self) -> bool { self.len() == 0
} /// Returns the number of days in the collection. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// assert_eq!(WeekdaySet::single(Mon).len(), 1); /// assert_eq!(WeekdaySet::from_array([Mon, Wed, Fri]).len(), 3); /// assert_eq!(WeekdaySet::ALL.len(), 7); /// ``` pubconstfn len(self) -> u8 { self.0.count_ones() as u8
}
/// An empty `WeekdaySet`. pubconst EMPTY: Self = Self(0b000_0000); /// A `WeekdaySet` containing all seven `Weekday`s. pubconst ALL: Self = Self(0b111_1111);
}
/// Print the underlying bitmask, padded to 7 bits. /// /// # Example /// ``` /// # use chrono::WeekdaySet; /// use chrono::Weekday::*; /// assert_eq!(format!("{:?}", WeekdaySet::single(Mon)), "WeekdaySet(0000001)"); /// assert_eq!(format!("{:?}", WeekdaySet::single(Tue)), "WeekdaySet(0000010)"); /// assert_eq!(format!("{:?}", WeekdaySet::ALL), "WeekdaySet(1111111)"); /// ``` impl Debug for WeekdaySet { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "WeekdaySet({:0>7b})", self.0)
}
}
/// An iterator over a collection of weekdays, starting from a given day. /// /// See [`WeekdaySet::iter()`]. #[derive(Debug, Clone)] pubstruct WeekdaySetIter {
days: WeekdaySet,
start: Weekday,
}
impl Iterator for WeekdaySetIter { type Item = Weekday;
// Split the collection in two at `start`. // Look for the first day among the days after `start` first, including `start` itself. // If there are no days after `start`, look for the first day among the days before `start`. let (before, after) = self.days.split_at(self.start); let days = if after.is_empty() { before } else { after };
let next = days.first().expect("the collection is not empty"); self.days.remove(next);
Some(next)
}
}
// Split the collection in two at `start`. // Look for the last day among the days before `start` first, NOT including `start` itself. // If there are no days before `start`, look for the last day among the days after `start`. let (before, after) = self.days.split_at(self.start); let days = if before.is_empty() { after } else { before };
let next_back = days.last().expect("the collection is not empty"); self.days.remove(next_back);
Some(next_back)
}
}
impl WeekdaySet { /// Iterate over all 128 possible sets, from `EMPTY` to `ALL`. fn iter_all() -> impl Iterator<Item = Self> {
(0b0000_0000..0b1000_0000).map(Self)
}
}
/// Panics if the 8-th bit of `self` is not 0. fn assert_8th_bit_invariant(days: WeekdaySet) {
assert!(days.0 & 0b1000_0000 == 0, "the 8-th bit of {days:?} is not 0");
}
#[test] fn bitwise_set_operations_preserve_8th_bit_invariant() { for set1 in WeekdaySet::iter_all() { for set2 in WeekdaySet::iter_all() {
assert_8th_bit_invariant(set1.union(set2));
assert_8th_bit_invariant(set1.intersection(set2));
assert_8th_bit_invariant(set1.symmetric_difference(set2));
}
}
}
/// Test `split_at` on all possible arguments. #[test] fn split_at_is_equivalent_to_iterating() { use Weekday::*;
// `split_at()` is used in `iter()`, so we must not iterate // over all days with `WeekdaySet::ALL.iter(Mon)`. const WEEK: [Weekday; 7] = [Mon, Tue, Wed, Thu, Fri, Sat, Sun];
for weekdays in WeekdaySet::iter_all() { for split_day in WEEK { let expected_before: WeekdaySet = WEEK
.into_iter()
.take_while(|&day| day != split_day)
.filter(|&day| weekdays.contains(day))
.collect(); let expected_after: WeekdaySet = WEEK
.into_iter()
.skip_while(|&day| day != split_day)
.filter(|&day| weekdays.contains(day))
.collect();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.