use chrono::{Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone}; use std::collections::HashMap; use uuid::Uuid; use crate::model::repetition::Repetition; use crate::model::time; use crate::validation; pub static DATE_FORMAT: &str = "%d/%m/%Y"; #[derive(Debug, Clone)] pub struct Event { pub id: Uuid, pub date: NaiveDate, pub start: Option, pub end: Option, pub name: String, pub repetition: Option, pub category: Option, } impl Event { pub fn pprint(&self) -> String { let start = self.start.map(time::pprint).unwrap_or_default(); let end = self .end .map(|t| format!("-{}", time::pprint(t))) .unwrap_or_default(); let space = if self.start.is_some() || self.end.is_some() { " " } else { "" }; format!("{}{}{}{}", start, end, space, self.name) } pub fn local_timestamp(&self) -> Option { let time = self.start.unwrap_or(NaiveTime::from_hms_opt(0, 0, 0)?); let naive_datetime = NaiveDateTime::new(self.date, time); let local_datetime = Local.from_local_datetime(&naive_datetime).single()?; Some(local_datetime.timestamp()) } } /// Recurring events in an date range (inclusive) pub fn repetitions_between( events: &[Event], start: NaiveDate, end: NaiveDate, ) -> HashMap> { let mut res: HashMap> = HashMap::new(); for event in events { if let Some(repetition) = &event.repetition { for date in repetition.between(event.date, start, end) { res.entry(date).or_insert_with(Vec::new).push(event.clone()) } } } res } // Validation pub fn validate( id: Uuid, date: String, name: String, start: String, end: String, repetition: Option, category: Option, ) -> Option { let start = validation::time(start)?; let end = validation::time(end)?; match (start, end) { (Some(s), Some(e)) if s > e => None?, _ => (), } Some(Event { id, date: NaiveDate::parse_from_str(&date, DATE_FORMAT).ok()?, name: validation::non_empty(name)?, start, end, repetition, category, }) }