use chrono::*;
use uuid::Uuid;
use std::collections::BTreeMap;
use std::fmt;
use std::mem;
use crate::properties::*;
#[derive(Clone, Copy, Debug)]
pub enum CalendarDateTime {
Floating(NaiveDateTime),
Utc(DateTime<Utc>),
}
impl fmt::Display for CalendarDateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
CalendarDateTime::Floating(naive_dt) => naive_dt.format("%Y%m%dT%H%M%S").fmt(f),
CalendarDateTime::Utc(utc_dt) => utc_dt.format("%Y%m%dT%H%M%SZ").fmt(f),
}
}
}
impl From<DateTime<Utc>> for CalendarDateTime {
fn from(dt: DateTime<Utc>) -> Self {
Self::Utc(dt)
}
}
impl From<NaiveDateTime> for CalendarDateTime {
fn from(dt: NaiveDateTime) -> Self {
Self::Floating(dt)
}
}
#[derive(Debug, Default)]
pub struct Event {
inner: InnerComponent,
}
#[derive(Debug, Default)]
pub struct Todo {
inner: InnerComponent,
}
#[derive(Debug, Default)]
pub struct Venue {
inner: InnerComponent,
}
#[derive(Debug, Default)]
struct InnerComponent {
properties: BTreeMap<String, Property>,
multi_properties: Vec<Property>,
}
impl InnerComponent {
pub fn done(&mut self) -> Self {
InnerComponent {
properties: mem::replace(&mut self.properties, BTreeMap::new()),
multi_properties: mem::replace(&mut self.multi_properties, Vec::new()),
}
}
}
impl Event {
pub fn new() -> Self {
Default::default()
}
pub fn done(&mut self) -> Self {
Event {
inner: self.inner.done(),
}
}
pub fn status(&mut self, status: EventStatus) -> &mut Self {
self.append_property(status.into());
self
}
}
impl Todo {
pub fn new() -> Self {
Default::default()
}
pub fn done(&mut self) -> Self {
Todo {
inner: self.inner.done(),
}
}
pub fn percent_complete(&mut self, percent: u8) -> &mut Self {
self.add_property("PERCENT-COMPLETE", &percent.to_string());
self
}
pub fn due<T: Into<CalendarDateTime>>(&mut self, dt: T) -> &mut Self {
let calendar_dt: CalendarDateTime = dt.into();
self.add_property("DUE", &calendar_dt.to_string());
self
}
pub fn completed(&mut self, dt: DateTime<Utc>) -> &mut Self {
self.add_property("COMPLETED", &CalendarDateTime::Utc(dt).to_string());
self
}
pub fn status(&mut self, status: TodoStatus) -> &mut Self {
self.append_property(status.into());
self
}
}
impl Venue {
pub fn new() -> Self {
Default::default()
}
pub fn done(&mut self) -> Self {
Venue {
inner: self.inner.done(),
}
}
pub fn street_address(&mut self, address: &str) -> &mut Self {
self.add_property("STREET-ADDRESS", address)
}
pub fn extended_address(&mut self, address: &str) -> &mut Self {
self.add_property("EXTENDED-ADDRESS", address)
}
pub fn locality(&mut self, locality: &str) -> &mut Self {
self.add_property("LOCALITY", locality)
}
pub fn region(&mut self, region: &str) -> &mut Self {
self.add_property("REGION", region)
}
pub fn country(&mut self, country: &str) -> &mut Self {
self.add_property("COUNTRY", country)
}
pub fn postal_code(&mut self, postal_code: &str) -> &mut Self {
self.add_property("POSTAL-CODE", postal_code)
}
}
pub trait Component {
fn component_kind() -> &'static str;
fn properties(&self) -> &BTreeMap<String, Property>;
fn multi_properties(&self) -> &Vec<Property>;
fn fmt_write<W: fmt::Write>(&self, out: &mut W) -> Result<(), fmt::Error> {
write_crlf!(out, "BEGIN:{}", Self::component_kind())?;
if !self.properties().contains_key("DTSTAMP") {
let now = CalendarDateTime::Utc(Utc::now());
write_crlf!(out, "DTSTAMP:{}", now)?;
}
for property in self.properties().values() {
property.fmt_write(out)?;
}
if !self.properties().contains_key("UID") {
write_crlf!(out, "UID:{}", Uuid::new_v4())?;
}
for property in self.multi_properties() {
property.fmt_write(out)?;
}
write_crlf!(out, "END:{}", Self::component_kind())?;
Ok(())
}
fn to_string(&self) -> String {
let mut out_string = String::new();
self.fmt_write(&mut out_string).unwrap();
out_string
}
fn append_property(&mut self, property: Property) -> &mut Self;
fn append_multi_property(&mut self, property: Property) -> &mut Self;
fn add_property(&mut self, key: &str, val: &str) -> &mut Self {
self.append_property(Property::new(key, val));
self
}
fn add_multi_property(&mut self, key: &str, val: &str) -> &mut Self {
self.append_multi_property(Property::new(key, val));
self
}
fn starts<T: Into<CalendarDateTime>>(&mut self, dt: T) -> &mut Self {
let calendar_dt = dt.into();
self.add_property("DTSTART", &calendar_dt.to_string());
self
}
fn ends<T: Into<CalendarDateTime>>(&mut self, dt: T) -> &mut Self {
let calendar_dt = dt.into();
self.add_property("DTEND", &calendar_dt.to_string());
self
}
fn start_date<TZ: TimeZone>(&mut self, date: Date<TZ>) -> &mut Self
where
TZ::Offset: fmt::Display,
{
self.append_property(
Property::new("DTSTART", date.format("%Y%m%d").to_string().as_ref())
.append_parameter(ValueType::Date)
.done(),
);
self
}
fn end_date<TZ: TimeZone>(&mut self, date: Date<TZ>) -> &mut Self
where
TZ::Offset: fmt::Display,
{
self.append_property(
Property::new("DTEND", date.format("%Y%m%d").to_string().as_ref())
.append_parameter(ValueType::Date)
.done(),
);
self
}
fn all_day<TZ: TimeZone>(&mut self, date: Date<TZ>) -> &mut Self
where
TZ::Offset: fmt::Display,
{
self.append_property(
Property::new("DTSTART", date.format("%Y%m%d").to_string().as_ref())
.append_parameter(ValueType::Date)
.done(),
)
.append_property(
Property::new("DTEND", date.format("%Y%m%d").to_string().as_ref())
.append_parameter(ValueType::Date)
.done(),
);
self
}
fn priority(&mut self, priority: u32) -> &mut Self {
let priority = ::std::cmp::min(priority, 10);
self.add_property("PRIORITY", &priority.to_string());
self
}
fn print(&self) -> Result<(), fmt::Error> {
let mut out = String::new();
self.fmt_write(&mut out)?;
print_crlf!("{}", out);
Ok(())
}
fn summary(&mut self, desc: &str) -> &mut Self {
self.add_property("SUMMARY", desc)
}
fn description(&mut self, desc: &str) -> &mut Self {
self.add_property("DESCRIPTION", desc)
}
fn location(&mut self, location: &str) -> &mut Self {
self.add_property("LOCATION", location);
self
}
fn venue(&mut self, location: &str, venue_uid: &str) -> &mut Self {
self.append_property(
Property::new("LOCATION", location)
.append_parameter(Parameter::new("VVENUE", venue_uid))
.done(),
);
self
}
fn uid(&mut self, uid: &str) -> &mut Self {
self.add_property("UID", uid);
self
}
fn class(&mut self, class: Class) -> &mut Self {
self.append_property(class.into())
}
}
macro_rules! component_impl {
($t:ty, $kind:expr) => {
impl Component for $t {
fn component_kind() -> &'static str {
$kind
}
fn properties(&self) -> &BTreeMap<String, Property> {
&self.inner.properties
}
fn multi_properties(&self) -> &Vec<Property> {
&self.inner.multi_properties
}
fn append_property(&mut self, property: Property) -> &mut Self {
self.inner
.properties
.insert(property.key().to_owned(), property);
self
}
fn append_multi_property(&mut self, property: Property) -> &mut Self {
self.inner.multi_properties.push(property);
self
}
}
};
}
component_impl! { Event, "VEVENT" }
component_impl! { Todo , "VTODO"}
component_impl! { Venue , "VVENUE"}