recurring_event 0.1.3

Calculate dates for recurring events (daily, weekly, monthly and yearly).
Documentation
use std::collections::{HashMap, hash_set::Iter};

use time::{Month, OffsetDateTime};

use crate::calendar::{Calendar, CalendarDay};
use crate::error::Error;
use crate::{Recurrences};
use crate::utility;

pub enum EventRecurrences<'a>{
    Multiple(&'a HashMap<Month, Recurrences>),
    Single(&'a Recurrences),
}

pub fn check_the_from_and_frequenced_periods(check_period: fn(OffsetDateTime, recurrences: &EventRecurrences) -> Result<Option<OffsetDateTime>, Error>, from_period: OffsetDateTime, frequenced_period: OffsetDateTime, event_recurrences: &EventRecurrences) -> Result<Option<OffsetDateTime>, Error> {
    let next_recurrence_date_ = check_period(from_period, event_recurrences);
    if next_recurrence_date_.is_ok() && next_recurrence_date_.as_ref().unwrap().is_none() {
        check_period(frequenced_period, event_recurrences)
    } else {
        next_recurrence_date_
    }
}

pub fn check_month(from_date: OffsetDateTime, event_recurrences: &EventRecurrences) -> Result<Option<OffsetDateTime>, Error> {
    let recurrences = match event_recurrences {
        EventRecurrences::Multiple(_) => Err(Error::UnexpectedValue)?,
        EventRecurrences::Single(recurrences) => recurrences,
    };

    let mut next_recurrence_dates = Vec::new();
    let to_date = utility::first_date_of_next_month(&from_date)?;
    let mut calendar = Calendar::new();
    calendar.create_days(from_date, to_date)?;

    let next_recurrence_date_ = match_recurrences(recurrences, Recurrences::weekdays, &calendar, CalendarDay::weekdays)?;
    if next_recurrence_date_.is_some() {
        next_recurrence_dates.push(next_recurrence_date_.unwrap());
    };

    let next_recurrence_date_ = match_recurrences(recurrences, Recurrences::monthly_days_ordinals, &calendar, CalendarDay::monthly_days_ordinals)?;
    if next_recurrence_date_.is_some() {
        next_recurrence_dates.push(next_recurrence_date_.unwrap());
    };

    let next_recurrence_date_ = match_recurrences(recurrences, Recurrences::monthly_weekdays_ordinals, &calendar, CalendarDay::monthly_weekdays_ordinals)?;
    if next_recurrence_date_.is_some() {
        next_recurrence_dates.push(next_recurrence_date_.unwrap());
    };

    if next_recurrence_dates.is_empty() {
        Ok(None)
    } else {
        Ok(next_recurrence_dates.iter().min().cloned())
    }
}

pub fn check_week(from_date: OffsetDateTime, event_recurrences: &EventRecurrences) -> Result<Option<OffsetDateTime>, Error> {
    let recurrences = match event_recurrences {
        EventRecurrences::Multiple(_) => Err(Error::UnexpectedValue)?,
        EventRecurrences::Single(recurrences) => recurrences,
    };

    let to_date = utility::first_date_of_next_week(&from_date)?;
    let mut calendar = Calendar::new();
    calendar.create_days(from_date, to_date)?;

    match_recurrences(recurrences, Recurrences::weekdays, &calendar, CalendarDay::weekdays)
}

pub fn check_year(from_date: OffsetDateTime, event_recurrences: &EventRecurrences) -> Result<Option<OffsetDateTime>, Error> {
    let recurrences_by_month = match event_recurrences {
        EventRecurrences::Multiple(recurrences) => recurrences,
        EventRecurrences::Single(_) => Err(Error::UnexpectedValue)?,
    };

    let mut month = from_date.month();
    /* Allow to enter the loop even if the from_date is in January */
    let mut first_pass = true;

    while first_pass || month != Month::January {
        let recurrences_ = recurrences_by_month.get(&month);

        if recurrences_.is_some() {
            let start_date = if month == from_date.month() {
                from_date
            } else {
                from_date.replace_day(1)?.replace_month(month)?
            };

            let next_recurrence_date_ = check_month(start_date, &EventRecurrences::Single(&recurrences_.unwrap()));

            if next_recurrence_date_.is_ok() && next_recurrence_date_.as_ref().unwrap().is_some() {
                return next_recurrence_date_;
            };
        };

        month = month.next();
        first_pass = false;
    };
    Ok(None)
}

fn match_recurrences<U: std::cmp::PartialEq>(recurrences: &Recurrences, event_recurrences_kind: fn(& Recurrences) -> Iter<U>, calendar: &Calendar, calendar_recurrence_kind: fn(& CalendarDay) -> &[U]) -> Result<Option<OffsetDateTime>, Error> {
    for day in calendar.days() {
        for event_recurrence_kind in event_recurrences_kind(recurrences) {
            if calendar_recurrence_kind(&day).contains(event_recurrence_kind) {
                return Ok(Some(day.date()))
            };
        };
    };
    Ok(None)
}