use crate::result::{Error, Result};
pub const YEAR_BASE: u16 = 2000;
pub const YEAR_MAX: u16 = 2255;
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Year(u8);
impl Year {
pub const fn new() -> Self {
Self(0)
}
pub const fn year(&self) -> u16 {
(self.0 as u16) + YEAR_BASE
}
#[allow(clippy::manual_range_contains)]
pub fn try_from_year(val: u16) -> Result<Self> {
match val {
v if v < YEAR_BASE || v > YEAR_MAX => Err(Error::InvalidVariant(val as usize)),
_ => Ok(Self(val.saturating_sub(YEAR_BASE) as u8)),
}
}
pub const fn bits(&self) -> u8 {
self.0
}
pub const fn from_bits(val: u8) -> Self {
Self(val)
}
}
impl Default for Year {
fn default() -> Self {
Self::new()
}
}
impl From<u8> for Year {
fn from(val: u8) -> Self {
Self::from_bits(val)
}
}
impl TryFrom<u16> for Year {
type Error = Error;
fn try_from(val: u16) -> Result<Self> {
Self::try_from_year(val)
}
}
impl From<Year> for u8 {
fn from(val: Year) -> Self {
val.bits()
}
}
impl From<Year> for u16 {
fn from(val: Year) -> Self {
val.year()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid() {
(0..=u8::MAX).for_each(|year| {
let exp_year = Year(year);
let adj_year = YEAR_BASE + (year as u16);
assert_eq!(Year::from_bits(year), exp_year);
assert_eq!(Year::try_from_year(adj_year), Ok(exp_year));
assert_eq!(exp_year.bits(), year);
assert_eq!(exp_year.year(), adj_year);
});
}
#[test]
fn test_invalid() {
(0..=u16::MAX)
.filter(|y| !(YEAR_BASE..=YEAR_MAX).contains(y))
.for_each(|year| {
assert_eq!(
Year::try_from_year(year),
Err(Error::InvalidVariant(year as usize))
);
});
}
}