diff --git a/src/context.rs b/src/context.rs index 5e9cf1d..f60a519 100644 --- a/src/context.rs +++ b/src/context.rs @@ -3,9 +3,9 @@ // SPDX-License-Identifier: MIT use crate::cli::Cli; +use crate::config::StyleType; use crate::config::{Config, Theme}; -use crate::config::{Style, StyleType}; -use crate::events::Event; +use crate::events::EventInstances; use anyhow::Result; use chrono::prelude::*; use clap::Parser; @@ -15,9 +15,9 @@ pub struct Context { pub usersetdate: chrono::NaiveDate, pub opts: Cli, pub config: Config, - pub eventstuple: Vec<(Event, Style)>, pub theme: Theme, pub styletype: StyleType, + pub eventinstances: EventInstances, } impl Context { @@ -45,9 +45,9 @@ impl Context { usersetdate, opts, config, - eventstuple: vec![], theme, styletype, + eventinstances: vec![], }) } } @@ -58,9 +58,9 @@ impl Default for Context { usersetdate: NaiveDate::default(), opts: Cli::default(), config: Config::default(), - eventstuple: vec![], theme: Theme::default(), styletype: StyleType::Light, + eventinstances: vec![], } } } @@ -72,11 +72,11 @@ mod tests { #[test] fn test_context_default() { let ctx = Context::default(); - assert!(ctx.eventstuple.is_empty()); + assert!(ctx.eventinstances.is_empty()); } #[test] fn test_context_new() { let ctx = Context::new().unwrap(); - assert!(ctx.eventstuple.is_empty()); + assert!(ctx.eventinstances.is_empty()); } } diff --git a/src/events/mod.rs b/src/events/mod.rs index 5202485..7d2a836 100644 --- a/src/events/mod.rs +++ b/src/events/mod.rs @@ -5,7 +5,10 @@ mod ics; pub use ics::ReadFromIcsFile; +use crate::config::Style; +use crate::utils::convertstyle; use chrono::prelude::*; +use chrono::Duration; use rrule::{RRuleSet, Tz}; use std::fmt; @@ -27,6 +30,22 @@ impl EventDateTime { } } +#[derive(Debug, Clone)] +pub struct EventInstance { + pub date: chrono::NaiveDate, + pub event: Event, + pub style: Style, +} + +impl fmt::Display for EventInstance { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let style = convertstyle(self.style.stylenames.to_vec(), "·"); + write!(f, "{} {}: {}", style, self.date, self.event.summary) + } +} + +pub type EventInstances = Vec; + #[derive(Debug, Clone)] pub struct Event { pub start: EventDateTime, @@ -35,6 +54,63 @@ pub struct Event { pub summary: String, } +impl Event { + pub fn instances(&self, start: &NaiveDate, end: &NaiveDate, style: &Style) -> EventInstances { + let timezone: Tz = Local::now().timezone().into(); + let before = timezone + .with_ymd_and_hms(end.year(), end.month(), end.day(), 23, 59, 59) + .unwrap(); + let after = timezone + .with_ymd_and_hms(start.year(), start.month(), start.day(), 0, 0, 0) + .unwrap(); + let duration = *end - *start; + let mut eventinstances: EventInstances = vec![]; + if self.rrulesets.is_empty() { + let mut date = self.start.date(); + if date == self.end.date() { + if start <= &date && &date <= end { + eventinstances.push(EventInstance { + date, + event: self.clone(), + style: style.clone(), + }); + } + } else { + while date < self.end.date() { + if start <= &date && &date <= end { + eventinstances.push(EventInstance { + date, + event: self.clone(), + style: style.clone(), + }); + } + date += Duration::days(1); + } + } + } else { + for rruleset in &self.rrulesets { + let ruleset = rruleset + .clone() + .before(before) + .after(after) + .all(duration.num_days() as u16); + eventinstances.append( + &mut ruleset + .dates + .iter() + .map(|date| EventInstance { + date: date.date_naive(), + event: self.clone(), + style: style.clone(), + }) + .collect::(), + ); + } + } + eventinstances + } +} + pub type Events = Vec; impl Default for Event { @@ -48,47 +124,6 @@ impl Default for Event { } } -impl Event { - pub fn is_day(&self, date: &chrono::NaiveDate) -> bool { - self.in_range(*date, *date) - } - - pub fn in_range( - &self, - daterangebegin: chrono::NaiveDate, - daterangeend: chrono::NaiveDate, - ) -> bool { - let timezone: Tz = Local::now().timezone().into(); - let before = timezone - .with_ymd_and_hms( - daterangeend.year(), - daterangeend.month(), - daterangeend.day(), - 23, - 59, - 59, - ) - .unwrap(); - let after = timezone - .with_ymd_and_hms( - daterangebegin.year(), - daterangebegin.month(), - daterangebegin.day(), - 0, - 0, - 0, - ) - .unwrap(); - for rruleset in &self.rrulesets { - let rresult = rruleset.clone().before(before).after(after).all(1); - if !rresult.dates.is_empty() { - return true; - } - } - daterangebegin <= self.start.date() && self.end.date() <= daterangeend - } -} - impl fmt::Display for Event { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let startformatstring = match self.start { @@ -120,45 +155,6 @@ mod tests { let date = NaiveDate::default(); assert_eq!(event.start, EventDateTime::Date(date)); } - - #[test] - fn test_event_is_day() { - let event = Event::default(); - let date = NaiveDate::default(); - assert!(event.is_day(&date)); - } - #[test] - fn test_event_is_yearly_day() { - let date = NaiveDate::default(); - let event = Event { - end: EventDateTime::Date(date), - ..Default::default() - }; - assert!(event.is_day(&date)); - } - #[test] - fn test_event_is_monthy_day() { - let event = Event { - ..Default::default() - }; - let date = NaiveDate::default(); - assert!(event.is_day(&date)); - } - #[test] - fn test_event_is_daily_day() { - let event = Event { - ..Default::default() - }; - let date = NaiveDate::default(); - assert!(event.is_day(&date)); - } - /*#[test] - fn test_event_get_end_date_case1() { - let mut event = Event::default(); - let date = NaiveDateTime::default(); - event.end = Some(EventDateTime::DateTime(date)); - assert_eq!(event.get_end_date(), date); - }*/ #[test] fn test_event_get_end_date_case2() { let date = NaiveDate::default(); diff --git a/src/main.rs b/src/main.rs index a8d745f..2ab5479 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,14 +62,16 @@ fn main() { months.push(ctx.usersetdate); } - // mabye we should use a pointer instead of cloning the style? for icalstyle in &ctx.config.ical { - let mut icsevents = Events::read_from_ics_file(&icalstyle.file); - icsevents.retain(|event| event.in_range(daterangebegin, daterangeend)); - for event in icsevents { - ctx.eventstuple.push((event, icalstyle.style.clone())); + for event in Events::read_from_ics_file(&icalstyle.file) { + ctx.eventinstances.append(&mut event.instances( + &daterangebegin, + &daterangeend, + &icalstyle.style, + )); } } + ctx.eventinstances.sort_by(|a, b| a.date.cmp(&b.date)); let calendar = Calendar { dates: months, diff --git a/src/output/agenda.rs b/src/output/agenda.rs index 5415238..384d03b 100644 --- a/src/output/agenda.rs +++ b/src/output/agenda.rs @@ -2,9 +2,7 @@ // // SPDX-License-Identifier: MIT -use crate::utils::convertstyle; use crate::Context; -use chrono::Datelike; use std::fmt; @@ -16,57 +14,10 @@ impl fmt::Display for Agenda<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut ret: String = String::new(); if self.ctx.opts.agenda { - ret = "Agenda\n".to_string(); - let eventstuple = &mut self.ctx.eventstuple.clone(); - eventstuple.sort_by(|(aevent, _), (bevent, _)| { - aevent - .start - .date() - .month() - .cmp(&bevent.start.date().month()) - .then(aevent.start.date().day().cmp(&bevent.start.date().day())) - }); - for (event, style) in eventstuple { - ret += format!( - "{} {}\n", - convertstyle(style.stylenames.to_vec(), "·"), - event - ) - .as_str(); + for pe in self.ctx.eventinstances.iter() { + ret += format!("{pe}\n").as_str(); } } write!(f, "{}", ret) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::config::Style; - use crate::events::{Event, EventDateTime}; - use chrono::{Duration, NaiveDate}; - - #[test] - fn test_fmt() { - let mut ctx = Context::default(); - ctx.opts.agenda = true; - let e1: Event = Event { - summary: String::from("Fake Event"), - ..Default::default() - }; - let e2: Event = Event { - start: EventDateTime::Date(NaiveDate::default() + Duration::weeks(56)), - summary: String::from("Fake Event"), - ..Default::default() - }; - let s1: Style = Style::default(); - let s2: Style = Style::default(); - ctx.eventstuple.push((e1, s1)); - ctx.eventstuple.push((e2, s2)); - let a = Agenda { ctx: &ctx }; - assert_eq![ - format!("{}", a), - String::from("Agenda\n· Thu, Jan, 1: Fake Event\n· Thu, Jan, 28: Fake Event\n") - ]; - } -} diff --git a/src/output/date.rs b/src/output/date.rs index 68e4e91..751ae67 100644 --- a/src/output/date.rs +++ b/src/output/date.rs @@ -29,7 +29,11 @@ impl Date<'_> { DateProperty::LastDayOfMonth => { self.date == self.firstdayofdisplayedmonth.last_day_of_month() } - DateProperty::IsEvent => self.is_event(), + DateProperty::IsEvent => self + .ctx + .eventinstances + .iter() + .any(|eventinstance| eventinstance.date == self.date), DateProperty::Monday => self.date.weekday() == chrono::Weekday::Mon, DateProperty::Tuesday => self.date.weekday() == chrono::Weekday::Tue, DateProperty::Wednesday => self.date.weekday() == chrono::Weekday::Wed, @@ -41,15 +45,6 @@ impl Date<'_> { DateProperty::Even => self.date.day() % 2 == 0, }) } - - fn is_event(&self) -> bool { - for (event, _) in &self.ctx.eventstuple { - if event.is_day(&self.date) { - return true; - } - } - false - } } impl fmt::Display for Date<'_> { @@ -70,9 +65,9 @@ impl fmt::Display for Date<'_> { .map(|datestyle| datestyle.style) .collect(); - for (event, style) in &self.ctx.eventstuple { - if event.is_day(&self.date) { - styles.push(style.clone()); + for eventinstance in &self.ctx.eventinstances { + if eventinstance.date == self.date { + styles.push(eventinstance.style.clone()); } } @@ -93,7 +88,6 @@ impl fmt::Display for Date<'_> { #[cfg(test)] mod tests { use super::*; - use crate::events::Event; use chrono::NaiveDate; #[test] @@ -151,25 +145,6 @@ mod tests { let s = String::from("015"); assert_eq!(format!("{}", d), s); } - #[test] - fn test_fmt_is_event() { - let date = NaiveDate::default(); - let mut ctx = Context::default(); - let firstdayofdisplayedmonth = NaiveDate::default(); - - let event = Event::default(); - let style = Style::default(); - ctx.eventstuple = vec![(event, style)]; - let d = Date { - date, - ctx: &ctx, - firstdayofdisplayedmonth, - }; - - let s = String::from("\u{1b}[1m\u{1b}[4m 1\u{1b}[0m"); - assert_eq!(format!("{}", d), s); - } - #[test] fn test_satisfy_firstdayofdisplayedmonth() { let date = NaiveDate::from_ymd_opt(1970, 2, 1).unwrap(); @@ -209,34 +184,4 @@ mod tests { let properties = [DateProperty::LastDayOfMonth]; assert!(d.satisfy_all(&properties)); } - #[test] - fn test_is_event() { - let date = NaiveDate::from_ymd_opt(1970, 1, 31).unwrap(); - let ctx = Context::default(); - let firstdayofdisplayedmonth = NaiveDate::default(); - let d = Date { - date, - ctx: &ctx, - firstdayofdisplayedmonth, - }; - assert!(!d.is_event()); - } - #[test] - fn test_satisfy_is_event() { - let date = NaiveDate::default(); - let mut ctx = Context::default(); - let firstdayofdisplayedmonth = NaiveDate::default(); - - let event = Event::default(); - let style = Style::default(); - ctx.eventstuple = vec![(event, style)]; - let d = Date { - date, - ctx: &ctx, - firstdayofdisplayedmonth, - }; - - let properties = [DateProperty::IsEvent]; - assert!(d.satisfy_all(&properties)); - } }