recurring_event 0.1.3

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

use time::{OffsetDateTime, Weekday, util};

use crate::error::Error;
use crate::utility;
use crate::{MonthlyDayOrdinal, MonthlyWeekdayOrdinal, Ordinal};

pub struct Calendar {
    days: Vec<CalendarDay>,
}

impl Calendar {
    pub fn create_days(&mut self, from_date: OffsetDateTime, to_date: OffsetDateTime) -> Result<(), Error> {
        let mut weekdays_counter = WeekDaysCounter::new();

        /* Week days ordinals only make sense from the beginning of the month.
        So, it's required to update WeekDaysCounter from 1st to from_date included */
        let mut date = utility::first_date_of_month(&from_date)?;

        while date < to_date {
            weekdays_counter.inc_counter(date.weekday());

            if date >= from_date {
                let weekday_ordinal = weekdays_counter.weekday_ordinal(date.weekday())?;
                self.insert_day(date, weekday_ordinal);
            };

            date = utility::next_day(&date)?;
        };
        Ok(())
    }

    pub fn days(&self) -> &Vec<CalendarDay> {
        &self.days
    }

    fn insert_day(&mut self, date: OffsetDateTime, weekday_ordinal: MonthlyWeekdayOrdinal) -> () {
        self.days.push(CalendarDay::new(date, weekday_ordinal));
    }

    pub fn new() -> Calendar {
        Calendar {
            days: Vec::new(),
        }
    }
}

#[derive (Debug)]
pub struct CalendarDay {
    date: OffsetDateTime,
    weekdays: [Weekday; 1],
    monthly_days_ordinals: [MonthlyDayOrdinal; 1],
    monthly_weekdays_ordinals: Vec<MonthlyWeekdayOrdinal>,
}

impl CalendarDay {
    pub fn date(&self) -> OffsetDateTime {
        self.date
    }

    pub fn monthly_days_ordinals(&self) -> &[MonthlyDayOrdinal] {
        &self.monthly_days_ordinals
    }

    pub fn monthly_weekdays_ordinals(&self) -> &[MonthlyWeekdayOrdinal] {
        &self.monthly_weekdays_ordinals
    }

    fn new(date: OffsetDateTime, monthly_weekday_ordinal: MonthlyWeekdayOrdinal) -> CalendarDay {
        let weekday = date.weekday();
        let monthly_day_ordinal = date.day();

        let mut monthly_weekdays_ordinals = Vec::new();
        monthly_weekdays_ordinals.push(monthly_weekday_ordinal);

        /* Check is this weekday is the last of the month. */
        if util::days_in_year_month(date.year(), date.month()) - monthly_day_ordinal < 7 {
            monthly_weekdays_ordinals.push(MonthlyWeekdayOrdinal::new(Ordinal::Last, weekday));
        };

        CalendarDay {
            date,
            weekdays: [weekday],
            monthly_days_ordinals: [monthly_day_ordinal],
            monthly_weekdays_ordinals : monthly_weekdays_ordinals,
        }
    }

    pub fn weekdays(&self) -> &[Weekday] {
        &self.weekdays
    }
}

#[derive (Debug)]
struct WeekDaysCounter {
    weekdays: HashMap<Weekday, u8>,
}

impl WeekDaysCounter {
    fn inc_counter(&mut self, weekday: Weekday) -> () {
        *self.weekdays.entry(weekday).or_insert(0) += 1;
    }

    fn new() -> WeekDaysCounter {
        WeekDaysCounter {
            weekdays: HashMap::new()
        }
    }

    fn weekday_ordinal(&self, weekday: Weekday) -> Result<MonthlyWeekdayOrdinal, Error> {
        let ordinals = HashMap::from([(1, Ordinal::First), (2, Ordinal::Second), (3, Ordinal::Third), (4, Ordinal::Fourth), (5, Ordinal::Fifth)]);

        let counter = self.weekdays.get(&weekday).ok_or(Error::NotFound)?;
        let ordinal = ordinals.get(&counter).ok_or(Error::UnexpectedValue)?;

        Ok(MonthlyWeekdayOrdinal::new(*ordinal, weekday))
    }
}