///|
pub fn event_key(event : Component) -> EventKey {
{
uid: event.get("UID").unwrap_or(""),
summary: event.get("SUMMARY").unwrap_or(""),
starts_at: event.get("DTSTART").unwrap_or(""),
ends_at: event.get("DTEND").unwrap_or(""),
rrule: event.get("RRULE").unwrap_or(""),
}
}
///|
pub fn diff_calendars(before : Calendar, after : Calendar) -> CalendarDiff {
let changes : Array[CalendarChange] = []
let before_events = before.events()
let after_events = after.events()
for old_event in before_events {
let old_key = event_key(old_event)
if old_key.uid.length() == 0 {
continue
}
match find_event(after_events, old_key.uid) {
None =>
changes.push({
kind: Removed,
uid: old_key.uid,
before: Some(event_signature(old_key)),
after: None,
breaking: true,
})
Some(new_event) => {
let new_key = event_key(new_event)
if event_signature(old_key) != event_signature(new_key) {
changes.push({
kind: Changed,
uid: old_key.uid,
before: Some(event_signature(old_key)),
after: Some(event_signature(new_key)),
breaking: false,
})
}
}
}
}
for new_event in after_events {
let key = event_key(new_event)
if key.uid.length() == 0 {
continue
}
match find_event(before_events, key.uid) {
None =>
changes.push({
kind: Added,
uid: key.uid,
before: None,
after: Some(event_signature(key)),
breaking: false,
})
Some(_) => ()
}
}
{ compatible: !has_breaking_calendar_change(changes), changes }
}
///|
pub fn render_diff(diff : CalendarDiff) -> String {
let b = StringBuilder::new()
b.write_string("# MoonCalGuard event diff\n\n")
b.write_string("- compatible: ")
b.write_string(diff.compatible.to_string())
b.write_string("\n- changes: ")
b.write_string(diff.changes.length().to_string())
b.write_string("\n\n")
if diff.changes.length() == 0 {
b.write_string("No event changes detected.\n")
return b.to_string()
}
for change in diff.changes {
b.write_string("- [")
b.write_string(change.kind.name())
b.write_string("] ")
b.write_string(change.uid)
if change.breaking {
b.write_string(" breaking")
}
match change.before {
Some(before) => {
b.write_string(" before=")
b.write_string(before)
}
None => ()
}
match change.after {
Some(after) => {
b.write_string(" after=")
b.write_string(after)
}
None => ()
}
b.write_string("\n")
}
b.to_string()
}
///|
fn find_event(events : Array[Component], uid : String) -> Component? {
for event in events {
match event.get("UID") {
Some(id) => if id == uid { return Some(event) }
None => ()
}
}
None
}
///|
fn event_signature(key : EventKey) -> String {
if key.rrule.length() == 0 {
"\{key.starts_at}..\{key.ends_at} \{key.summary}"
} else {
"\{key.starts_at}..\{key.ends_at} \{key.summary} rrule=\{key.rrule}"
}
}
///|
fn has_breaking_calendar_change(changes : Array[CalendarChange]) -> Bool {
for change in changes {
if change.breaking {
return true
}
}
false
}