use chrono::{DateTime, Datelike, Local, TimeZone};
use icalendar::{Component, DatePerhapsTime};
use regex::Regex;
use std::cmp::{max, min};
use super::date_range::CalTempsDateRange;
#[derive(Debug, Clone)]
pub struct CalTempsEntry {
pub is_event: bool,
pub summary: String,
pub description: String,
pub start: Option<DateTime<Local>>,
pub end: Option<DateTime<Local>>,
}
impl CalTempsEntry {
pub fn duration_minutes(&self) -> Option<i64> {
match self.start {
Some(ds) => self.end.map(|de| (de - ds).num_minutes()),
_ => None,
}
}
pub fn date_range_intersection_minutes(&self, date_range: &CalTempsDateRange) -> Option<i64> {
match self.start {
Some(ds) => match self.end {
Some(de) => {
let mins = (match date_range.end {
Some(dre) => min(de, dre),
_ => de,
} - match date_range.start {
Some(drs) => max(ds, drs),
_ => ds,
})
.num_minutes();
if mins < 0 { None } else { Some(mins) }
}
_ => None,
},
_ => None,
}
}
fn get_matches(&self, pattern: &str) -> Vec<&str> {
let re = Regex::new(pattern).unwrap();
let mut v: Vec<_> = re.find_iter(&self.summary).map(|m| m.as_str()).collect();
v.sort_by_cached_key(|t| t.to_lowercase());
v
}
pub fn full_tags(&self) -> Vec<&str> {
self.hash_tags()
.into_iter()
.chain(self.who_tags())
.collect()
}
pub fn at_tags(&self) -> Vec<&str> {
self.get_matches(r"([@][\w]+)")
}
pub fn hash_tags(&self) -> Vec<&str> {
self.get_matches(r"([#][\w]+)")
}
pub fn plus_tags(&self) -> Vec<&str> {
self.get_matches(r"([+][\w]+)")
}
pub fn minus_tags(&self) -> Vec<&str> {
self.get_matches(r"([-][\w]+)")
}
pub fn star_tags(&self) -> Vec<&str> {
self.get_matches(r"([*][\w]+)")
}
pub fn who_tags(&self) -> Vec<&str> {
self.get_matches(r"([!][\w]+)")
}
}
fn dpt_to_dt(dpt: Option<DatePerhapsTime>) -> Option<DateTime<Local>> {
match dpt {
Some(icalendar::DatePerhapsTime::DateTime(cdt)) => cdt.try_into_utc().map(|d| d.into()),
Some(icalendar::DatePerhapsTime::Date(nd)) => Some(
Local
.with_ymd_and_hms(nd.year(), nd.month(), nd.day(), 0, 0, 0)
.unwrap(),
),
_ => None,
}
}
fn get_matching_entry_basic(
active_filter: String,
date_range: &CalTempsDateRange,
summary: Option<&str>,
description: Option<&str>,
start: Option<DateTime<Local>>,
end: Option<DateTime<Local>>,
) -> Option<CalTempsEntry> {
if let Some(summary_str) = summary {
if summary_str.contains(&active_filter) {
return match start {
Some(dts) => {
if date_range.start.unwrap_or(dts) <= dts {
match end {
Some(dte) => {
if dte <= date_range.end.unwrap_or(dte) {
Some(CalTempsEntry {
summary: summary_str.into(),
description: description.unwrap_or_default().into(),
start,
end,
is_event: true,
})
} else {
None
}
}
_ => None,
}
} else {
None
}
}
_ => None,
};
}
}
None
}
pub fn get_matching_entry(
component: icalendar::CalendarComponent,
active_filter: String,
date_range: &CalTempsDateRange,
) -> Option<CalTempsEntry> {
match component {
icalendar::CalendarComponent::Event(item) => get_matching_entry_basic(
active_filter,
date_range,
item.get_summary(),
item.get_description(),
dpt_to_dt(item.get_start()),
dpt_to_dt(item.get_end()),
),
_ => None,
}
}