Import upstream v0.16.22, stripped
Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
commit
7dae9b29fd
1650 files changed
+485521
No files matched your search
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{
|
||||
ArchivedCalendarEventData, ArchivedTimezone, CalendarEventData, Timezone,
|
||||
alarm::{CalendarAlarm, ExpandAlarm},
|
||||
};
|
||||
use crate::calendar::{ComponentTimeRange, alarm::CalendarAlarmType};
|
||||
use calcard::{
|
||||
common::timezone::Tz,
|
||||
icalendar::{ICalendar, ICalendarComponentType, dates::TimeOrDelta},
|
||||
};
|
||||
use compact_str::ToCompactString;
|
||||
use indexmap::IndexMap;
|
||||
use store::{
|
||||
ahash::{AHashMap, RandomState},
|
||||
write::{key::KeySerializer, now},
|
||||
};
|
||||
|
||||
const MAX_TIME_SPAN: i64 = u32::MAX as i64;
|
||||
|
||||
impl CalendarEventData {
|
||||
pub fn new(
|
||||
ical: ICalendar,
|
||||
default_tz: Tz,
|
||||
max_expansions: usize,
|
||||
next_email_alarm: &mut Option<CalendarAlarm>,
|
||||
) -> Self {
|
||||
let mut ranges = TimeRanges::default();
|
||||
let now = now() as i64;
|
||||
|
||||
let expanded = ical.expand_dates(default_tz, max_expansions);
|
||||
let mut groups: IndexMap<(u16, u16, u16, i32), Vec<i64>, RandomState> =
|
||||
IndexMap::with_capacity_and_hasher(16, RandomState::default());
|
||||
let mut alarms = AHashMap::with_capacity(16);
|
||||
|
||||
for event in expanded.events {
|
||||
let start_naive = event.start.naive_local();
|
||||
let start_tz = event.start.timezone().as_id();
|
||||
let start_timestamp_utc = event.start.timestamp();
|
||||
let start_timestamp_naive = start_naive.and_utc().timestamp();
|
||||
let (end_timestamp_utc, end_timestamp_naive, end_tz) = match event.end {
|
||||
TimeOrDelta::Time(time) => {
|
||||
let end_naive = time.naive_local();
|
||||
let end_timestamp_utc = time.timestamp();
|
||||
let end_timestamp_naive = end_naive.and_utc().timestamp();
|
||||
(
|
||||
end_timestamp_utc,
|
||||
end_timestamp_naive,
|
||||
time.timezone().as_id(),
|
||||
)
|
||||
}
|
||||
TimeOrDelta::Delta(delta) => {
|
||||
let delta = delta.num_seconds();
|
||||
(
|
||||
start_timestamp_utc + delta,
|
||||
start_timestamp_naive + delta,
|
||||
start_tz,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Expand alarms
|
||||
let mut min = std::cmp::min(start_timestamp_utc, end_timestamp_utc);
|
||||
let mut max = std::cmp::max(start_timestamp_utc, end_timestamp_utc);
|
||||
for alarm in alarms.entry(event.comp_id).or_insert_with(|| {
|
||||
ical.component_by_id(event.comp_id)
|
||||
.map_or(&[][..], |c| c.component_ids.as_slice())
|
||||
.iter()
|
||||
.filter_map(|alarm_id| {
|
||||
ical.component_by_id(*alarm_id).and_then(|alarm| {
|
||||
if alarm.component_type == ICalendarComponentType::VAlarm {
|
||||
alarm.expand_alarm(*alarm_id as u16, event.comp_id as u16)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}) {
|
||||
if let Some(alarm_time) =
|
||||
alarm
|
||||
.delta
|
||||
.to_timestamp(start_timestamp_utc, end_timestamp_utc, default_tz)
|
||||
{
|
||||
if alarm_time < min {
|
||||
min = alarm_time;
|
||||
}
|
||||
if alarm_time > max {
|
||||
max = alarm_time;
|
||||
}
|
||||
if alarm_time > now
|
||||
&& next_email_alarm
|
||||
.as_ref()
|
||||
.is_none_or(|next| alarm_time < next.alarm_time)
|
||||
{
|
||||
*next_email_alarm = Some(CalendarAlarm {
|
||||
alarm_id: alarm.id,
|
||||
event_id: alarm.parent_id,
|
||||
alarm_time,
|
||||
typ: if alarm.is_email_alert {
|
||||
CalendarAlarmType::Email {
|
||||
event_start: start_timestamp_naive,
|
||||
event_end: end_timestamp_naive,
|
||||
event_start_tz: start_tz,
|
||||
event_end_tz: end_tz,
|
||||
}
|
||||
} else {
|
||||
CalendarAlarmType::Display {
|
||||
recurrence_id: if ical.components[alarm.parent_id as usize]
|
||||
.is_recurrent_or_override()
|
||||
{
|
||||
start_timestamp_naive.into()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ranges.update_base_offset(start_timestamp_naive, end_timestamp_naive);
|
||||
ranges.update_utc_min_max(min, max);
|
||||
groups
|
||||
.entry((
|
||||
start_tz,
|
||||
end_tz,
|
||||
event.comp_id as u16,
|
||||
(end_timestamp_naive - start_timestamp_naive)
|
||||
.clamp(i32::MIN as i64, i32::MAX as i64) as i32,
|
||||
))
|
||||
.or_default()
|
||||
.push(start_timestamp_naive);
|
||||
}
|
||||
|
||||
let mut events = Vec::with_capacity(groups.len());
|
||||
for ((start_tz, end_tz, id, duration), mut instances) in groups {
|
||||
instances.sort_unstable();
|
||||
instances.truncate(instances.partition_point(|instance| {
|
||||
instance.saturating_sub(ranges.base_offset) <= MAX_TIME_SPAN
|
||||
}));
|
||||
|
||||
let instances = match instances.len() {
|
||||
0 => continue,
|
||||
1 => KeySerializer::new(std::mem::size_of::<u32>())
|
||||
.write_leb128((instances[0] - ranges.base_offset) as u32)
|
||||
.finalize(),
|
||||
len => {
|
||||
// Bitpack instances
|
||||
let mut instance_offsets = Vec::with_capacity(len);
|
||||
for instance in instances {
|
||||
debug_assert!(instance >= ranges.base_offset);
|
||||
instance_offsets.push((instance - ranges.base_offset) as u32);
|
||||
}
|
||||
|
||||
KeySerializer::new(instance_offsets.len() * std::mem::size_of::<u32>())
|
||||
.bitpack_sorted(&instance_offsets)
|
||||
.finalize()
|
||||
}
|
||||
};
|
||||
|
||||
events.push(ComponentTimeRange {
|
||||
id,
|
||||
start_tz,
|
||||
end_tz,
|
||||
duration,
|
||||
instances: instances.into_boxed_slice(),
|
||||
});
|
||||
}
|
||||
|
||||
if !expanded.errors.is_empty() {
|
||||
trc::event!(
|
||||
Calendar(trc::CalendarEvent::RuleExpansionError),
|
||||
Reason = expanded
|
||||
.errors
|
||||
.into_iter()
|
||||
.map(|e| e.error.to_compact_string())
|
||||
.collect::<Vec<_>>(),
|
||||
Details = ical.to_string(),
|
||||
Limit = max_expansions,
|
||||
);
|
||||
}
|
||||
|
||||
CalendarEventData {
|
||||
event: ical,
|
||||
time_ranges: events.into_boxed_slice(),
|
||||
alarms: alarms
|
||||
.into_values()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
base_offset: ranges.base_offset,
|
||||
base_time_utc: (ranges.min_time_utc - ranges.base_offset).clamp(0, MAX_TIME_SPAN)
|
||||
as u32,
|
||||
duration: (ranges.max_time_utc - ranges.min_time_utc).clamp(0, MAX_TIME_SPAN) as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_range(&self) -> Option<(i64, u32)> {
|
||||
if self.base_offset != 0 {
|
||||
Some((self.base_offset + self.base_time_utc as i64, self.duration))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct TimeRanges {
|
||||
max_time_utc: i64,
|
||||
min_time_utc: i64,
|
||||
base_offset: i64,
|
||||
}
|
||||
|
||||
impl TimeRanges {
|
||||
pub fn update_base_offset(&mut self, t1: i64, t2: i64) {
|
||||
let offset = std::cmp::min(t1, t2);
|
||||
if offset < self.base_offset || self.base_offset == 0 {
|
||||
self.base_offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_utc_min_max(&mut self, min: i64, max: i64) {
|
||||
if min < self.min_time_utc || self.min_time_utc == 0 {
|
||||
self.min_time_utc = min;
|
||||
}
|
||||
if max > self.max_time_utc {
|
||||
self.max_time_utc = max;
|
||||
}
|
||||
if min < self.base_offset || self.base_offset == 0 {
|
||||
self.base_offset = min;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchivedCalendarEventData {
|
||||
pub fn event_range(&self) -> Option<(i64, u32)> {
|
||||
if self.base_offset != 0 {
|
||||
Some((
|
||||
self.base_offset.to_native() + self.base_time_utc.to_native() as i64,
|
||||
self.duration.to_native(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_range_start(&self) -> i64 {
|
||||
self.base_offset.to_native() + self.base_time_utc.to_native() as i64
|
||||
}
|
||||
|
||||
pub fn event_range_end(&self) -> i64 {
|
||||
self.base_offset.to_native()
|
||||
+ self.base_time_utc.to_native() as i64
|
||||
+ self.duration.to_native() as i64
|
||||
}
|
||||
}
|
||||
|
||||
impl CalendarEventData {
|
||||
pub fn event_range_start(&self) -> i64 {
|
||||
self.base_offset + self.base_time_utc as i64
|
||||
}
|
||||
|
||||
pub fn event_range_end(&self) -> i64 {
|
||||
self.base_offset + self.base_time_utc as i64 + self.duration as i64
|
||||
}
|
||||
}
|
||||
|
||||
impl Timezone {
|
||||
pub fn tz(&self) -> Option<Tz> {
|
||||
match self {
|
||||
Timezone::IANA(iana) => Tz::from_id(*iana),
|
||||
Timezone::Custom(icalendar) => icalendar
|
||||
.timezones()
|
||||
.filter_map(|t| t.timezone().map(|x| x.1))
|
||||
.next(),
|
||||
Timezone::Default => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchivedTimezone {
|
||||
pub fn tz(&self) -> Option<Tz> {
|
||||
match self {
|
||||
ArchivedTimezone::IANA(iana) => Tz::from_id(iana.to_native()),
|
||||
ArchivedTimezone::Custom(icalendar) => icalendar
|
||||
.timezones()
|
||||
.filter_map(|t| t.timezone().map(|x| x.1))
|
||||
.next(),
|
||||
ArchivedTimezone::Default => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user