Skip to content

iCalendar/JSCalendar and vCard/JSContact parsing, building and conversion library for Rust

Notifications You must be signed in to change notification settings

stalwartlabs/calcard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

66 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

calcard

crates.io build docs.rs crates.io

calcard is a Rust crate for parsing, generating, and converting calendaring and contact data across multiple formats. It supports iCalendar and vCard, making it easy to work with .ics and .vcf files. It also fully handles JSCalendar and JSContact, the emerging standards commonly used in JMAP-based systems. In addition to parsing and serializing these formats, calcard provides seamless conversion between iCalendar and JSCalendar, as well as between vCard and JSContact, enabling smooth interoperability between traditional and modern calendaring and contact protocols.

In general, this library abides by the Postel's law or Robustness Principle which states that an implementation must be conservative in its sending behavior and liberal in its receiving behavior. This means that calcard will make a best effort to parse non-conformant iCal/vCard/JSCalendar/JSContact objects as long as these do not deviate too much from the standard.

Features

Comprehensive features for working with calendaring and contact data, including:

  • iCalendar support: Parsing and generating iCalendar data.
  • vCard support: Parsing and generating vCard data.
  • JSCalendar support: Parsing and generating JSCalendar data.
  • JSContact support: Parsing and generating JSContact data.
  • Seamless conversion between iCalendar and JSCalendar.
  • Seamless conversion between vCard and JSContact.
  • Recurrence rules expansion: Accurately computes and enumerates repeating events based on iCalendar and JSCalendar RRULEs.
  • IANA timezone detection: Automatically resolves and handles custom and proprietary timezones.

Usage

Parsing an iCalendar and/or vCard stream

You can parse vCard and iCalendar files using the stream-based parser:

let input = "BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nEND:VCARD\n";
let mut parser = Parser::new(&input);

loop {
    match parser.entry() {
        Entry::VCard(vcard) => println!("Parsed VCard: {:?}", vcard),
        Entry::ICalendar(ical) => println!("Parsed ICalendar: {:?}", ical),
        Entry::InvalidLine(line) => eprintln!("Invalid line found: {}", line),
        Entry::UnexpectedComponentEnd { expected, found } => {
            eprintln!("Unexpected end: expected {:?}, found {:?}", expected, found);
        },
        Entry::UnterminatedComponent(component) => {
            eprintln!("Unterminated component: {:?}", component);
        },
        Entry::Eof => {
            break;
        }
    }
}

Parsing iCalendar

You can parse a single iCalendar using the ICalendar::parse method:

let input = "BEGIN:VCALENDAR\nVERSION:2.0\nEND:VCALENDAR\n";
let ical = ICalendar::parse(&input);

println!("Parsed ICalendar: {:?}", ical);

Parsing vCard

You can parse a single vCard using the VCard::parse method:

let input = "BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nEND:VCARD\n";
let vcard = VCard::parse(&input);

println!("Parsed VCard: {:?}", vcard);

Parsing JSCalendar

You can parse a JSCalendar JSON string using the JSCalendar::parse method:

let input = r#"{
                    "@type": "Group",
                    "uid": "bf0ac22b-4989-4caf-9ebd-54301b4ee51a",
                    "updated": "2020-01-15T18:00:00Z",
                    "title": "A simple group",
                    "entries": [{
                        "@type": "Event",
                        "uid": "a8df6573-0474-496d-8496-033ad45d7fea",
                        "updated": "2020-01-02T18:23:04Z",
                        "title": "Some event",
                        "start": "2020-01-15T13:00:00",
                        "timeZone": "America/New_York",
                        "duration": "PT1H"
                    },
                    {
                        "@type": "Task",
                        "uid": "2a358cee-6489-4f14-a57f-c104db4dc2f2",
                        "updated": "2020-01-09T14:32:01Z",
                        "title": "Do something"
                    }]
                }"#;

let jscalendar = JSCalendar::parse(input).unwrap();
println!("Parsed JSCalendar: {}", jscalendar.to_string_pretty());

Parsing JSContact

You can parse a JSContact JSON string using the JSContact::parse method:

let input = r#"{
                    "@type": "Card",
                    "version": "1.0",
                    "uid": "22B2C7DF-9120-4969-8460-05956FE6B065",
                    "kind": "individual",
                    "name": {
                    "components": [
                        { "kind": "given", "value": "John" },
                        { "kind": "surname", "value": "Doe" }
                    ],
                    "isOrdered": true
                    }
                }"#;

let jscontact = JSContact::parse(input).unwrap();
println!("Parsed JSContact: {}", jscontact.to_string_pretty());

Converting to/from iCalendar and JSCalendar

To convert a JSCalendar to an iCalendar, use the JSCalendar::into_icalendar method:

let ical = JSCalendar::parse(input).unwrap().into_icalendar().unwrap();

To convert an iCalendar to a JSCalendar, use the ICalendar::into_jscalendar method:

let jscalendar = ICalendar::parse(&input).unwrap().into_jscalendar().unwrap();

Converting to/from vCard and JSContact

To convert a JSContact to a vCard, use the JSContact::into_vcard method:

let vcard = JSContact::parse(input).unwrap().into_vcard().unwrap();

To convert a vCard to a JSContact, use the VCard::into_jscontact method:

let jscontact = VCard::parse(&input).unwrap().into_jscontact().unwrap();

Generating iCalendar and vCard

To generate a vCard or iCalendar, simply call .to_string() on the parsed object:

let vcard_string = vcard.to_string();
println!("Generated vCard:\n{}", vcard_string);

let ical_string = ical.to_string();
println!("Generated ICalendar:\n{}", ical_string);

Note: Documentation for creating VCard and ICalendar objects is coming soon.

Generating JSCalendar and JSContact

To generate a JSCalendar or JSContact, simply call .to_string() on the parsed object or .to_string_pretty() for a more human-readable format:

let jscalendar_string = jscalendar.to_string();
println!("Generated JSCalendar:\n{}", jscalendar_string);

let jscontact_string = jscontact.to_string();
println!("Generated JSContact:\n{}", jscontact_string);

Note: Documentation for creating JSCalendar and JSContact objects is coming soon.

Testing and Fuzzing

To run the testsuite:

 $ cargo test --all-features

or, to run the testsuite with MIRI:

 $ cargo +nightly miri test --all-features

To fuzz the library with cargo-fuzz:

 $ cargo +nightly fuzz run fuzz_all
 $ cargo +nightly fuzz run fuzz_random
 $ cargo +nightly fuzz run fuzz_structured

License

Licensed under either of

at your option.

Funding

Part of the development of this library was funded through the NGI Zero Core, a fund established by NLnet with financial support from the European Commission's programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 101092990.

If you find this library useful you can help by becoming a sponsor. Thank you!

Copyright

Copyright (C) 2020, Stalwart Labs LLC

About

iCalendar/JSCalendar and vCard/JSContact parsing, building and conversion library for Rust

Topics

Resources

Code of conduct

Security policy

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

  •  

Languages