Skip to content

AdamPerlinski/ezcal

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ezcal

crates.io docs.rs license GitHub stars

Docs & Examples

You don't need a 33K LOC enterprise library to read a calendar file.

Most apps just need to create a meeting invite, parse an .ics export, or read a contacts file. You shouldn't need to understand RFC 5545 to do that.

ezcal is iCalendar + vCard in one crate. Builder pattern in, valid .ics / .vcf out. Parse, modify, write back — unknown properties preserved.

cargo add ezcal

What Can You Do With It?

Create a Meeting Invite

Got a meeting? Turn it into a .ics file any calendar app can open.

use ezcal::ical::{Calendar, Event};

let cal = Calendar::new()
    .event(
        Event::new()
            .summary("Team Standup")
            .location("Room 42")
            .starts("2026-03-15T09:00:00")
            .ends("2026-03-15T09:30:00")
    )
    .build();

std::fs::write("meeting.ics", cal.to_string())?;
// → Opens in Google Calendar, Apple Calendar, Outlook

Parse Calendar Exports

Got a .ics file from Google Calendar? Read it in two lines.

use ezcal::ical::Calendar;

let calendar = Calendar::parse(&std::fs::read_to_string("export.ics")?)?;
for event in calendar.events() {
    println!("{}: {}",
        event.get_starts().unwrap(),
        event.get_summary().unwrap_or("(untitled)")
    );
}

Build a Contact Card

Create .vcf files that import into any contacts app.

use ezcal::vcard::Contact;

let card = Contact::new()
    .full_name("Jane Doe")
    .email("jane@example.com")
    .phone("+1-555-0123")
    .organization("Acme Corp")
    .build();

std::fs::write("jane.vcf", card.to_string())?;

Read a Contacts File

Parse .vcf exports — even files with hundreds of contacts.

use ezcal::vcard::Contact;

let contacts = Contact::parse_all(&std::fs::read_to_string("contacts.vcf")?)?;
for c in &contacts {
    println!("{}: {}", c.get_full_name().unwrap(), c.get_email().unwrap_or("(no email)"));
}

When to Use What?

You Want To Use This Result
Create a calendar event Calendar::new().event(Event::new()...) Valid .ics file
Parse an .ics file Calendar::parse(&text) Typed Calendar struct
Create a contact card Contact::new().full_name(...) Valid .vcf file
Parse a .vcf file Contact::parse_all(&text) Vec of Contact structs
Add recurring events .rrule(RecurrenceRule::parse("FREQ=WEEKLY")?) RRULE in your event
Add reminders .alarm(Alarm::display("-PT15M", "Soon!")) VALARM in your event
Work with timezones .starts_dt(DateTimeValue::DateTimeTz{...}) TZID parameter set
Convert to chrono dt.to_chrono_utc() DateTime<Utc>

Real-World Use Cases

1. Appointment Booking System

Problem: "Users book appointments, I need to send them a calendar invite."

use ezcal::ical::{Calendar, Event, Alarm};

fn create_booking(title: &str, start: &str, end: &str, location: &str) -> String {
    Calendar::new()
        .event(
            Event::new()
                .summary(title)
                .location(location)
                .starts(start)
                .ends(end)
                .status("CONFIRMED")
                .alarm(Alarm::display("-PT15M", "Appointment starting soon"))
        )
        .build()
        .to_string()
}

// Attach to email or serve as HTTP response
let ics = create_booking("Haircut", "2026-03-15T10:00:00", "2026-03-15T10:30:00", "Main St Salon");

2. Calendar Sync / Migration

Problem: "Export from one calendar, import to another."

use ezcal::ical::Calendar;

let cal = Calendar::parse(&std::fs::read_to_string("google-export.ics")?)?;

println!("Migrating {} events, {} todos", cal.events().len(), cal.todos().len());

for event in cal.events() {
    let summary = event.get_summary().unwrap_or("(untitled)");
    let start = event.get_starts().map(|d| d.to_string()).unwrap_or_default();
    // Insert into your new system...
    println!("  {} @ {}", summary, start);
}

3. Contact Import/Export

Problem: "Let users export their contacts as a .vcf file."

use ezcal::vcard::{Contact, Address, StructuredName};

fn export_contacts(users: &[User]) -> String {
    let mut output = String::new();
    for user in users {
        let card = Contact::new()
            .full_name(&user.name)
            .email(&user.email)
            .phone(&user.phone)
            .organization(&user.company)
            .build();
        output.push_str(&card.to_string());
    }
    output
}

4. Recurring Event Schedules

Problem: "Set up a weekly standup that repeats for a year."

use ezcal::ical::{Calendar, Event, RecurrenceRule};

let cal = Calendar::new()
    .event(
        Event::new()
            .summary("Weekly Standup")
            .starts("2026-01-05T09:00:00")
            .ends("2026-01-05T09:30:00")
            .rrule(RecurrenceRule::parse("FREQ=WEEKLY;BYDAY=MO;COUNT=52").unwrap())
            .add_category("MEETING")
    )
    .build();

5. Todo List Export

Problem: "Export tasks to a format other apps can read."

use ezcal::ical::{Calendar, Todo};

let cal = Calendar::new()
    .todo(Todo::new().summary("Ship v1.0").due_date("2026-04-01").priority(1).status("NEEDS-ACTION"))
    .todo(Todo::new().summary("Write docs").due_date("2026-03-15").priority(3).status("IN-PROCESS"))
    .todo(Todo::new().summary("Fix bug #42").status("COMPLETED").completed_date("2026-03-01"))
    .build();

std::fs::write("tasks.ics", cal.to_string())?;

API Reference

iCalendar (ezcal::ical)

Type What It Does Key Methods
Calendar .ics container new(), parse(), event(), todo(), build()
Event VEVENT component summary(), starts(), ends(), location(), rrule(), alarm()
Todo VTODO component summary(), due_date(), status(), priority(), percent_complete()
Alarm VALARM component display(trigger, desc), audio(trigger)
RecurrenceRule RRULE parse(str), new(Frequency)

vCard (ezcal::vcard)

Type What It Does Key Methods
Contact .vcf container new(), parse(), parse_all(), full_name(), email(), phone()
StructuredName N property new(family, given), with_prefix(), with_suffix()
Address ADR property new(), street(), city(), region(), postal_code(), country()

DateTime (ezcal::datetime)

Type What It Does Key Methods
DateTimeValue Date/time values parse(), to_chrono_utc(), from_chrono_utc(), to_property()

Comparison

Library iCal vCard Read + Write Status
ezcal Yes Yes Yes Active
ical Yes No Read only Archived (Aug 2024)
icalendar Yes No Write-focused Active
calcard Yes Yes Yes 33K LOC, complex API

Compatibility

Output tested against:

  • Google Calendar (.ics import)
  • Apple Calendar (.ics import)
  • Outlook (.ics import)
  • Google Contacts (.vcf import)
  • Apple Contacts (.vcf import)

Links

License

MIT

About

Ergonomic iCalendar + vCard library for Rust

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

 

Packages

 
 
 

Contributors

Languages