use std::{
collections::HashMap,
ops::{Add, Sub},
};
use chrono::{DateTime, Duration, NaiveDate, NaiveTime, Utc};
use derive_more::From;
use crate::{
generator::Emitter,
parser::{ContentLine, ParserError},
types::{CalDate, CalDateTime, Tz, Value},
};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, From)]
pub enum CalDateOrDateTime {
DateTime(CalDateTime),
Date(CalDate),
}
impl CalDateOrDateTime {
pub fn parse_prop(
prop: &ContentLine,
timezones: Option<&HashMap<String, Option<chrono_tz::Tz>>>,
default_type: &str,
) -> Result<Self, ParserError> {
Ok(match prop.params.get_value_type().unwrap_or(default_type) {
"DATE" => Self::Date(CalDate::parse_prop(prop, timezones)?),
"DATE-TIME" => Self::DateTime(CalDateTime::parse_prop(prop, timezones)?),
_ => return Err(ParserError::InvalidPropertyType(prop.generate())),
})
}
pub fn is_date(&self) -> bool {
matches!(self, Self::Date(_))
}
pub fn date_floor(&self) -> NaiveDate {
match self {
Self::DateTime(datetime) => datetime.date_floor(),
Self::Date(date) => date.naive_date().to_owned(),
}
}
pub fn date_ceil(&self) -> NaiveDate {
match self {
Self::DateTime(datetime) => datetime.date_ceil(),
Self::Date(date) => date.naive_date().to_owned(),
}
}
pub fn timezone(&self) -> Tz {
match self {
Self::DateTime(datetime) => datetime.timezone(),
Self::Date(date) => *date.timezone(),
}
}
pub fn utc(&self) -> DateTime<Utc> {
match self {
Self::DateTime(datetime) => datetime.utc(),
Self::Date(date) => date.naive_date().and_time(NaiveTime::default()).and_utc(),
}
}
pub fn format(&self) -> String {
match self {
Self::DateTime(datetime) => datetime.format(),
Self::Date(date) => date.format(),
}
}
pub fn value_type(&self) -> &'static str {
match self {
Self::DateTime(_) => "DATE-TIME",
Self::Date(_) => "DATE",
}
}
}
impl Sub<&CalDateOrDateTime> for CalDateOrDateTime {
type Output = Duration;
fn sub(self, rhs: &CalDateOrDateTime) -> Self::Output {
self.utc() - rhs.utc()
}
}
impl From<CalDateOrDateTime> for CalDateTime {
fn from(value: CalDateOrDateTime) -> Self {
match value {
CalDateOrDateTime::DateTime(datetime) => datetime,
CalDateOrDateTime::Date(date) => date.as_datetime().into(),
}
}
}
impl From<CalDateOrDateTime> for DateTime<Tz> {
fn from(value: CalDateOrDateTime) -> Self {
match value {
CalDateOrDateTime::DateTime(datetime) => datetime.0,
CalDateOrDateTime::Date(date) => date.as_datetime(),
}
}
}
impl Add<Duration> for CalDateOrDateTime {
type Output = CalDateTime;
fn add(self, duration: Duration) -> Self::Output {
CalDateTime::from(self) + duration
}
}
impl Value for CalDateOrDateTime {
fn value_type(&self) -> Option<&'static str> {
match self {
CalDateOrDateTime::DateTime(datetime) => datetime.value_type(),
CalDateOrDateTime::Date(date) => date.value_type(),
}
}
fn value(&self) -> String {
match self {
CalDateOrDateTime::DateTime(datetime) => datetime.value(),
CalDateOrDateTime::Date(date) => date.value(),
}
}
fn utc_or_local(self) -> Self {
match self {
Self::DateTime(datetime) => Self::DateTime(datetime.utc_or_local()),
Self::Date(date) => Self::Date(date.utc_or_local()),
}
}
}