iCalendar (RFC 5545) parsing, recurrence expansion, and a minimal CalDAV server in pure MoonBit
Dependencies
moon add justinwongcn/moon-ical@0.1.0///|
test {
let events = @moon_ical.parse_events(
"BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nUID:demo\r\n" +
"DTSTART:20260901T090000Z\r\nRRULE:FREQ=DAILY;COUNT=3\r\n" +
"END:VEVENT\r\nEND:VCALENDAR\r\n",
)
let occurrences = @moon_ical.expand_series(events)
assert_eq(occurrences.length(), 3)
assert_eq(occurrences[2].start.to_string(), "2026-09-03T09:00:00Z")
}moon run demo --target native
moon run demo --target native -- https://example.com/calendar.ics
moon run cmd/serve --target native -- ./caldata 8437moon check --target all --deny-warn
moon test --target all --deny-warn
moon fmt --check
moon info
python tools/s5_acceptance.py
python tools/s6_acceptance.py| Client or driver | Discovery | Read/list | Create/update/delete | Result |
|---|---|---|---|---|
| Real curl over TCP | Yes | Yes | Yes | 32/32 live checks pass |
| Thunderbird 155.0.1 (Windows) | Yes | Yes | Yes | Passed against localhost: seed read, create, in-place update, and delete verified in vdir |
| DAVx5 | Not run | Not run | Not run | Android device required |
| Apple Calendar | Not run | Not run | Not run | macOS/iOS device required |
fn test_example() raise {
let rule = @rrule.parse_rule("FREQ=DAILY;COUNT=3")
let dtstart = @model.parse_single_date_time(
"20260901T090000Z",
@model.ZoneTable::empty(),
)
let occurrences = @rrule.expand(rule, dtstart)
assert_eq(occurrences.length(), 3)
assert_eq(occurrences[2].to_string(), "2026-09-03T09:00:00Z")
}fn test_example() raise {
let events = @model.parse_events(
"BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nSUMMARY:Sync\r\nEND:VEVENT\r\nEND:VCALENDAR",
)
assert_eq(events.length(), 1)
assert_eq(events[0].summary, Some("Sync"))
}fn test_example() raise {
let rule = @rrule.parse_rule("FREQ=WEEKLY;BYDAY=TU,TH;COUNT=10")
assert_true(rule.freq is @rrule.Freq::Weekly)
assert_eq(rule.count, Some(10))
assert_eq(rule.byday.length(), 2)
}fn test_example() raise {
let roots = @model.parse_components([
"BEGIN:VCALENDAR", "SUMMARY:Sync", "END:VCALENDAR",
])
assert_eq(
@serialize.serialize_components(roots),
"BEGIN:VCALENDAR\r\nSUMMARY:Sync\r\nEND:VCALENDAR\r\n",
)
}Install
Download zipiCalendar (RFC 5545) parsing, recurrence expansion, and a minimal CalDAV server in pure MoonBit
Dependencies