use std::fmt::{Display, Formatter};
use thiserror::Error;
use time::{ext::NumericalDuration, OffsetDateTime};
use timext::ext::NumericCalendarDuration;
#[derive(Debug, Error)]
#[error("not a valid change frequency variant")]
pub struct FrequencyError;
#[derive(Debug, Clone, PartialEq)]
pub enum Frequency {
Always,
Hourly,
Daily,
Weekly,
Monthly,
Yearly,
Never,
}
impl Frequency {
pub fn parse(frequency: &str) -> Result<Self, FrequencyError> {
let frequency = frequency.trim().to_lowercase();
use Frequency::*;
match frequency.as_str() {
"always" => Ok(Always),
"hourly" => Ok(Hourly),
"daily" => Ok(Daily),
"weekly" => Ok(Weekly),
"monthly" => Ok(Monthly),
"yearly" => Ok(Yearly),
"never" => Ok(Never),
_ => Err(FrequencyError),
}
}
pub fn next_date(&self, date: OffsetDateTime) -> Option<OffsetDateTime> {
use Frequency::*;
match &self {
Always | Never => None,
Hourly => Some(date + 1.hours()),
Daily => Some(date + 1.days()),
Weekly => Some(date + 7.days()),
Monthly => Some(date + 1.months()),
Yearly => Some(date + 1.years()),
}
}
pub fn is_outdated(&self, date: OffsetDateTime, now: OffsetDateTime) -> bool {
match &self {
Self::Always => true,
Self::Never => false,
_ => match self.next_date(date) {
Some(next) => next <= now,
None => unreachable!(),
},
}
}
}
impl Display for Frequency {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let underlying = match self {
Self::Always => "always",
Self::Hourly => "hourly",
Self::Daily => "daily",
Self::Weekly => "weekly",
Self::Monthly => "monthly",
Self::Yearly => "yearly",
Self::Never => "never",
};
Display::fmt(underlying, f)
}
}
impl TryFrom<&str> for Frequency {
type Error = FrequencyError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::parse(value)
}
}