icalendar/parser/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
//! # Parsing iCalendar document parser
//!
//! I would have loved to provide a zero-copy parser here however the *Internet Calendaring and Scheduling Core Object Specification (iCalendar)*
//! allows, nay demands special line folding:
//! > Lines of text SHOULD NOT be longer than 75 octets, excluding the line break. Long content lines SHOULD be split into a multiple line representations using a line "folding" technique.
//! > -- [rfc5545 3.1]
//!
//! For this reason parsing iCal is a bit indirect.
//! In this module you find the following functions to parser iCalendar document.
//! [`unfold()`] will unfold the iCal content and turn it into the nice machine-readable format it ought to be.
//! [`read_calendar_simple()`] returns a Vector of [`Component`]s
//! [`read_calendar()`] does the same thing but produces nicer parsing errors with line numbers (referencing the normalized content).
//!
//! You don't have to use `normalize()` on your document if your calendar does not obey the folding rules specified in [rfc5545 3.1].
//! If it unexpectedly does, the errors might be a tad confusing.
//!
//!
//! [rfc5545 3.1]: https://datatracker.ietf.org/doc/html/rfc5545#section-3.1
//!
//! A Calendar is always a tree of [`Component`]s.
//! It may contain multiple root elements so we have a `Vec<Component>` and each `Component` may more child `Component`s.
//! Each [`Component`] has properties, so a [`Vec`] of [`Property`] and those have of [`Parameter`]s.
//!
//!
#![allow(missing_docs)]
use nom::{error::convert_error, error::VerboseError, Finish};
mod calendar;
pub(crate) mod components;
mod parameters;
mod parsed_string;
mod properties;
#[cfg(test)]
mod tests;
mod utils;
pub use calendar::Calendar;
pub use components::Component;
pub use parameters::Parameter;
pub use parsed_string::ParseString;
pub use properties::Property;
use components::*;
pub use utils::unfold;
/// Parse iCalendar file content into an array of [`Component`]s
///
/// This version produces very simple Errors for simplicity's sake.
pub fn read_calendar_simple(input: &str) -> Result<Vec<Component<'_>>, nom::error::Error<&str>> {
components(input).finish().map(|(_, components)| components)
}
/// Parse iCalendar file content into an array of [`Component`]s
///
/// This version produces nice and readable errors with line numbers thanks the the awesomeness of [`nom`].
/// Line numbers are in regard to the normalized/unfolded version of the input, so better keep those around for reference.
///
pub fn read_calendar(input: &str) -> Result<Calendar<'_>, String> {
components(input)
.finish()
.map(|(_, mut components)| {
let root_is_calendar = components
.first()
.map(|first_root| first_root.name == "VCALENDAR")
.unwrap_or(false);
if root_is_calendar {
let root = components.swap_remove(0);
Calendar {
properties: root.properties,
components: root.components,
}
} else {
Calendar {
components,
properties: Vec::new(),
}
}
})
.map_err(|e: VerboseError<&str>| format!("error: {}", convert_error(input, e.clone())))
}
#[test]
fn begin_crash() {
assert!(read_calendar("BEGIN:").is_ok());
}
/// Parse iCalendar file content into an array of [`Component`]s
///
/// This version produces nice and readable errors with line numbers thanks the the awesomeness of [`nom`].
/// Line numbers are in regard to the normalized/unfolded version of the input, so better keep those around for reference.
///
pub fn read_components(input: &str) -> Result<Vec<Component<'_>>, String> {
components(input)
.finish()
.map(|(_, components)| components)
.map_err(|e: VerboseError<&str>| format!("error: {}", convert_error(input, e.clone())))
}