use super::{
ICalendar, ICalendarComponent, ICalendarComponentType, ICalendarEntry, ICalendarProperty,
ICalendarValue,
};
use crate::{common::timezone::Tz, icalendar::ICalendarParameterName};
use std::{collections::HashMap, str::FromStr};
pub struct TzResolver<T> {
tzs: HashMap<T, Tz>,
default: Tz,
}
impl<T> TzResolver<T>
where
T: std::borrow::Borrow<str> + std::hash::Hash + Eq,
{
pub fn resolve(&self, tz_name: &str) -> Option<Tz> {
self.tzs
.get(tz_name)
.copied()
.or_else(|| Tz::from_str(tz_name).ok())
}
pub fn resolve_or_default(&self, tz_name: Option<&str>) -> Tz {
tz_name
.and_then(|tz_name| {
self.tzs
.get(tz_name)
.copied()
.or_else(|| Tz::from_str(tz_name).ok())
})
.unwrap_or(self.default)
}
pub fn with_default(mut self, default: impl Into<Tz>) -> Self {
self.default = default.into();
self
}
}
impl ICalendar {
pub fn timezones(&self) -> impl Iterator<Item = &ICalendarComponent> {
self.components
.iter()
.filter(|comp| matches!(comp.component_type, ICalendarComponentType::VTimezone))
}
pub fn is_timezone(&self) -> bool {
self.timezones().count() == 1
}
pub fn build_tz_resolver(&self) -> TzResolver<&'_ str> {
TzResolver {
tzs: self.timezones().filter_map(|tz| tz.timezone()).collect(),
default: Tz::Floating,
}
}
pub fn build_owned_tz_resolver(&self) -> TzResolver<String> {
TzResolver {
tzs: self
.timezones()
.filter_map(|tz| tz.timezone())
.map(|(name, tz)| (name.to_string(), tz))
.collect(),
default: Tz::Floating,
}
}
}
impl ICalendarComponent {
pub fn timezone(&self) -> Option<(&str, Tz)> {
let mut tz_name = None;
let mut tz_id = None;
let mut tz_lic = None;
let mut tz_cdo_id = None;
for entry in &self.entries {
match (&entry.name, entry.values.first()) {
(ICalendarProperty::Tzid, Some(ICalendarValue::Text(id))) => {
tz_id = Tz::from_str(id).ok();
tz_name = Some(id.as_str());
}
(ICalendarProperty::Other(value), Some(ICalendarValue::Text(id))) => {
hashify::fnc_map!(value.as_bytes(),
"X-LIC-LOCATION" => {
tz_lic = Tz::from_str(id.strip_prefix("SystemV/").unwrap_or(id.as_str())).ok();
},
"X-MICROSOFT-CDO-TZID" => {
tz_cdo_id = Tz::from_ms_cdo_zone_id(id);
},
_ => {}
);
}
_ => (),
}
}
tz_name.and_then(|name| tz_id.or(tz_lic).or(tz_cdo_id).map(|tz| (name, tz)))
}
}
impl ICalendarEntry {
pub fn tz_id(&self) -> Option<&str> {
self.parameters(&ICalendarParameterName::Tzid)
.find_map(|v| v.as_text())
}
}