use crate::{TimeError, Year};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct YearRange {
from: Year,
to: Year,
}
impl YearRange {
pub const ALWAYS: Self = Self {
from: Year::MIN,
to: Year::MAX,
};
#[must_use]
pub const fn from_year(from: Year) -> Self {
Self {
from,
to: Year::MAX,
}
}
#[must_use]
pub const fn through(to: Year) -> Self {
Self {
from: Year::MIN,
to,
}
}
pub const fn try_between(from: Year, to: Year) -> Result<Self, TimeError> {
if to.get() < from.get() {
Err(TimeError::InvalidYearRange)
} else {
Ok(Self { from, to })
}
}
#[must_use]
pub const fn literal_through(to: u16) -> Self {
Self {
from: Year::MIN,
to: Year::literal(to),
}
}
#[must_use]
#[allow(clippy::panic)]
pub const fn literal_between(from: u16, to: u16) -> Self {
let from = Year::literal(from);
let to = Year::literal(to);
assert!(
to.get() >= from.get(),
"YearRange::literal_between: to < from",
);
Self { from, to }
}
#[must_use]
pub const fn start(self) -> Year {
self.from
}
#[must_use]
pub const fn end(self) -> Year {
self.to
}
#[must_use]
pub const fn contains(&self, year: Year) -> bool {
year.get() >= self.from.get() && year.get() <= self.to.get()
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn always_covers_full_range() {
let r = YearRange::ALWAYS;
assert!(r.contains(Year::MIN));
assert!(r.contains(Year::MAX));
}
#[test]
fn from_year_is_unbounded_above() {
let r = YearRange::from_year(Year::new(2021).unwrap());
assert!(!r.contains(Year::new(2020).unwrap()));
assert!(r.contains(Year::new(2021).unwrap()));
assert!(r.contains(Year::MAX));
}
#[test]
fn through_is_unbounded_below() {
let r = YearRange::through(Year::new(2020).unwrap());
assert!(r.contains(Year::MIN));
assert!(r.contains(Year::new(2020).unwrap()));
assert!(!r.contains(Year::new(2021).unwrap()));
}
#[test]
fn try_between_enforces_ordering() {
assert_eq!(
YearRange::try_between(Year::new(2020).unwrap(), Year::new(2019).unwrap()),
Err(TimeError::InvalidYearRange),
);
let r = YearRange::try_between(Year::new(2020).unwrap(), Year::new(2020).unwrap()).unwrap();
assert!(r.contains(Year::new(2020).unwrap()));
assert!(!r.contains(Year::new(2019).unwrap()));
assert!(!r.contains(Year::new(2021).unwrap()));
}
}