use chrono::Timelike; use chrono::{NaiveDate, NaiveTime}; use std::collections::HashMap; use uuid::Uuid; use crate::model::repetition::Repetition; 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 fn init(date: NaiveDate) -> Event { Event { id: Uuid::new_v4(), date, start: None, end: None, name: "".to_string(), repetition: None, } } impl Event { pub fn pprint(&self) -> String { let start = self.start.map(pprint_time).unwrap_or_default(); let end = self .end .map(|t| format!("-{}", pprint_time(t))) .unwrap_or_default(); let space = if self.start.is_some() || self.end.is_some() { " " } else { "" }; format!("{}{}{}{}", start, end, space, self.name) } } /// Repeated events in an included date range 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 } pub fn pprint_time(t: NaiveTime) -> String { if t.minute() == 0 { format!("{}h", t.hour()) } else { format!("{}h{}", t.hour(), t.minute()) } } fn parse_time(t: &str) -> Option { match t.split('h').collect::>()[..] { [hours, minutes] => { if minutes.trim().is_empty() { NaiveTime::from_hms_opt(hours.parse().ok()?, 0, 0) } else { NaiveTime::from_hms_opt(hours.parse().ok()?, minutes.parse().ok()?, 0) } } _ => None, } } // Validation pub fn validate( id: Uuid, date: String, name: String, start: String, end: String, repetition: Option, ) -> Option { let start = validate_time(start)?; let end = validate_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: validate_name(name)?, start, end, repetition, }) } fn validate_time(time: String) -> Option> { let time = time.trim(); if time.is_empty() { Some(None) } else { parse_time(time).map(Some) } } fn validate_name(name: String) -> Option { let name = name.trim(); if name.is_empty() { None } else { Some(name.to_string()) } }