mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 13:32:16 +00:00
Move to own ical-rs fork and refactor timezone-related stuff
This commit is contained in:
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -1598,11 +1598,11 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ical"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7cab7543a8b7729a19e2c04309f902861293dcdae6558dfbeb634454d279f6"
|
||||
source = "git+https://github.com/lennart-k/ical-rs#44209d8fbdde7a8f9ec42e90a91a90b2b46ed1f0"
|
||||
dependencies = [
|
||||
"chrono-tz",
|
||||
"serde",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -95,7 +95,11 @@ strum = "0.27"
|
||||
strum_macros = "0.27"
|
||||
serde_json = { version = "1.0", features = ["raw_value"] }
|
||||
sqlx-sqlite = { version = "0.8", features = ["bundled"] }
|
||||
ical = { version = "0.11", features = ["generator", "serde"] }
|
||||
ical = { git = "https://github.com/lennart-k/ical-rs", features = [
|
||||
"generator",
|
||||
"serde",
|
||||
"chrono-tz",
|
||||
] }
|
||||
toml = "0.9"
|
||||
tower = "0.5"
|
||||
tower-http = { version = "0.6", features = [
|
||||
|
||||
@@ -63,7 +63,6 @@ pub async fn route_get<C: CalendarStore, S: SubscriptionStore>(
|
||||
params: None,
|
||||
});
|
||||
}
|
||||
let mut ical_calendar = ical_calendar_builder.build();
|
||||
|
||||
for object in &objects {
|
||||
match object.get_data() {
|
||||
@@ -73,17 +72,21 @@ pub async fn route_get<C: CalendarStore, S: SubscriptionStore>(
|
||||
..
|
||||
}) => {
|
||||
timezones.extend(object_timezones);
|
||||
ical_calendar.events.push(event.clone());
|
||||
ical_calendar_builder = ical_calendar_builder.add_event(event.clone());
|
||||
}
|
||||
CalendarObjectComponent::Todo(TodoObject { todo, .. }) => {
|
||||
ical_calendar.todos.push(todo.clone());
|
||||
ical_calendar_builder = ical_calendar_builder.add_todo(todo.clone());
|
||||
}
|
||||
CalendarObjectComponent::Journal(JournalObject { journal, .. }) => {
|
||||
ical_calendar.journals.push(journal.clone());
|
||||
ical_calendar_builder = ical_calendar_builder.add_journal(journal.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ical_calendar = ical_calendar_builder
|
||||
.build()
|
||||
.map_err(|parser_error| Error::IcalError(parser_error.into()))?;
|
||||
|
||||
let mut resp = Response::builder().status(StatusCode::OK);
|
||||
let hdrs = resp.headers_mut().unwrap();
|
||||
hdrs.typed_insert(ContentType::from_str("text/calendar").unwrap());
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
use crate::Error;
|
||||
use crate::{CalDateTime, ComponentMut, parse_duration};
|
||||
use crate::{CalDateTime, parse_duration};
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use ical::{
|
||||
generator::IcalEvent,
|
||||
parser::{Component, ical::component::IcalTimeZone},
|
||||
property::Property,
|
||||
};
|
||||
use ical::parser::ComponentMut;
|
||||
use ical::{generator::IcalEvent, parser::Component, property::Property};
|
||||
use rrule::{RRule, RRuleSet};
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EventObject {
|
||||
pub event: IcalEvent,
|
||||
pub timezones: HashMap<String, IcalTimeZone>,
|
||||
pub timezones: HashMap<String, Option<chrono_tz::Tz>>,
|
||||
pub(crate) ics: String,
|
||||
}
|
||||
|
||||
@@ -128,7 +125,7 @@ impl EventObject {
|
||||
} else {
|
||||
date.format()
|
||||
};
|
||||
let mut ev = self.event.clone();
|
||||
let mut ev = self.event.clone().mutable();
|
||||
ev.remove_property("RRULE");
|
||||
ev.remove_property("RDATE");
|
||||
ev.remove_property("EXDATE");
|
||||
@@ -163,7 +160,7 @@ impl EventObject {
|
||||
params: dtstart_prop.params,
|
||||
});
|
||||
}
|
||||
events.push(ev);
|
||||
events.push(ev.verify()?);
|
||||
}
|
||||
Ok(events)
|
||||
} else {
|
||||
|
||||
@@ -4,10 +4,7 @@ use crate::Error;
|
||||
use chrono::DateTime;
|
||||
use chrono::Utc;
|
||||
use derive_more::Display;
|
||||
use ical::{
|
||||
generator::{Emitter, IcalCalendar},
|
||||
parser::{Component, ical::component::IcalTimeZone},
|
||||
};
|
||||
use ical::generator::{Emitter, IcalCalendar};
|
||||
use serde::Serialize;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{collections::HashMap, io::BufReader};
|
||||
@@ -90,15 +87,11 @@ impl CalendarObject {
|
||||
));
|
||||
}
|
||||
|
||||
let timezones: HashMap<String, IcalTimeZone> = cal
|
||||
let timezones: HashMap<String, Option<chrono_tz::Tz>> = cal
|
||||
.timezones
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter_map(|timezone| {
|
||||
let timezone_prop = timezone.get_property("TZID")?.to_owned();
|
||||
let tzid = timezone_prop.value?;
|
||||
Some((tzid, timezone))
|
||||
})
|
||||
.map(|timezone| (timezone.get_tzid().to_owned(), (&timezone).try_into().ok()))
|
||||
.collect();
|
||||
|
||||
if let Some(event) = cal.events.first() {
|
||||
@@ -161,11 +154,7 @@ impl CalendarObject {
|
||||
}
|
||||
|
||||
pub fn get_component_name(&self) -> &str {
|
||||
match self.data {
|
||||
CalendarObjectComponent::Todo(_) => "VTODO",
|
||||
CalendarObjectComponent::Event(_) => "VEVENT",
|
||||
CalendarObjectComponent::Journal(_) => "VJOURNAL",
|
||||
}
|
||||
self.get_object_type().as_str()
|
||||
}
|
||||
|
||||
pub fn get_object_type(&self) -> CalendarObjectType {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
mod property_ext;
|
||||
pub use property_ext::*;
|
||||
|
||||
mod timestamp;
|
||||
mod timezone;
|
||||
pub use timestamp::*;
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
use ical::{generator::IcalEvent, property::Property};
|
||||
|
||||
pub trait IcalProperty {
|
||||
fn get_param(&self, name: &str) -> Option<Vec<&str>>;
|
||||
fn get_value_type(&self) -> Option<&str>;
|
||||
fn get_tzid(&self) -> Option<&str>;
|
||||
}
|
||||
|
||||
impl IcalProperty for ical::property::Property {
|
||||
fn get_param(&self, name: &str) -> Option<Vec<&str>> {
|
||||
self.params
|
||||
.as_ref()?
|
||||
.iter()
|
||||
.find(|(key, _)| name == key)
|
||||
.map(|(_, value)| value.iter().map(String::as_str).collect())
|
||||
}
|
||||
|
||||
fn get_value_type(&self) -> Option<&str> {
|
||||
self.get_param("VALUE")
|
||||
.and_then(|params| params.into_iter().next())
|
||||
}
|
||||
|
||||
fn get_tzid(&self) -> Option<&str> {
|
||||
self.get_param("TZID")
|
||||
.and_then(|params| params.into_iter().next())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ComponentMut {
|
||||
fn remove_property(&mut self, name: &str);
|
||||
fn set_property(&mut self, prop: Property);
|
||||
fn push_property(&mut self, prop: Property);
|
||||
}
|
||||
|
||||
impl ComponentMut for IcalEvent {
|
||||
fn remove_property(&mut self, name: &str) {
|
||||
self.properties.retain(|prop| prop.name != name);
|
||||
}
|
||||
fn set_property(&mut self, prop: Property) {
|
||||
self.remove_property(&prop.name);
|
||||
self.push_property(prop);
|
||||
}
|
||||
fn push_property(&mut self, prop: Property) {
|
||||
self.properties.push(prop);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
use super::timezone::CalTimezone;
|
||||
use crate::IcalProperty;
|
||||
use chrono::{DateTime, Datelike, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc};
|
||||
use chrono_tz::Tz;
|
||||
use derive_more::derive::Deref;
|
||||
use ical::{
|
||||
parser::{Component, ical::component::IcalTimeZone},
|
||||
property::Property,
|
||||
};
|
||||
use ical::property::Property;
|
||||
use lazy_static::lazy_static;
|
||||
use rustical_xml::{ValueDeserialize, ValueSerialize};
|
||||
use std::{borrow::Cow, collections::HashMap, ops::Add};
|
||||
@@ -136,7 +132,7 @@ impl Add<Duration> for CalDateTime {
|
||||
impl CalDateTime {
|
||||
pub fn parse_prop(
|
||||
prop: &Property,
|
||||
timezones: &HashMap<String, IcalTimeZone>,
|
||||
timezones: &HashMap<String, Option<chrono_tz::Tz>>,
|
||||
) -> Result<Self, CalDateTimeError> {
|
||||
let prop_value = prop
|
||||
.value
|
||||
@@ -145,28 +141,9 @@ impl CalDateTime {
|
||||
"empty property".to_owned(),
|
||||
))?;
|
||||
|
||||
// Use the TZID parameter from the property
|
||||
let timezone = if let Some(tzid) = prop.get_tzid() {
|
||||
let timezone = if let Some(tzid) = prop.get_param("TZID") {
|
||||
if let Some(timezone) = timezones.get(tzid) {
|
||||
// X-LIC-LOCATION is often used to refer to a standardised timezone from the Olson
|
||||
// database
|
||||
if let Some(olson_name) = timezone
|
||||
.get_property("X-LIC-LOCATION")
|
||||
.map(|prop| prop.value.to_owned())
|
||||
.unwrap_or_default()
|
||||
{
|
||||
if let Ok(tz) = olson_name.parse::<Tz>() {
|
||||
Some(tz)
|
||||
} else {
|
||||
return Err(CalDateTimeError::InvalidOlson(olson_name));
|
||||
}
|
||||
} else {
|
||||
// If the TZID matches a name from the Olson database (e.g. Europe/Berlin) we
|
||||
// guess that we can just use it
|
||||
tzid.parse::<Tz>().ok()
|
||||
// TODO: If None: Too bad, we need to manually parse it
|
||||
// For now it's just treated as localtime
|
||||
}
|
||||
timezone.to_owned()
|
||||
} else {
|
||||
// TZID refers to timezone that does not exist
|
||||
return Err(CalDateTimeError::InvalidTZID(tzid.to_string()));
|
||||
|
||||
Reference in New Issue
Block a user