justinwongcn/moon-ical/ical/text does not have a README file

    ParseError

    pub(all) suberror ParseError {
    BadLine(line_no~ : Int, line~ : String, message~ : String)
    BadRule(rule~ : String, message~ : String)
    } derive(Eq,
    Debug
    )

    A line-level iCalendar parse failure.

    BadLine is raised by the content-line and component layers: line_no is 1-based over logical (unfolded) lines and line keeps the offending text, so a caller can point a user at the exact place a feed broke instead of just reporting "invalid iCalendar". BadRule is raised by value-level parsers such as @rrule.parse_rule, where a whole RRULE value is the unit of failure and no line context applies.
    impl Show for ParseError

    ParseError::equal

    fn ParseError::equal(ParseError, ParseError) -> Bool

    ParseError::not_equal

    fn ParseError::not_equal(x : ParseError, y : ParseError) -> Bool

    ParseError::output

    fn ParseError::output(self : ParseError, logger : &Logger) -> Unit

    ParseError::to_string

    fn ParseError::to_string(self : ParseError) -> String

    ContentLine

    pub struct ContentLine {
    name : String
    params : Array[PropertyParam]
    value : String
    } derive(Eq,
    Debug
    )

    One parsed property line: NAME;PARAM=VAL;PARAM2="A,B":value.

    value is still the escaped form as it appears on the wire; call [unescape_value] to get the text a human should see.

    ContentLine::equal

    fn ContentLine::equal(ContentLine, ContentLine) -> Bool

    ContentLine::not_equal

    fn ContentLine::not_equal(x : ContentLine, y : ContentLine) -> Bool

    ContentLine::param

    fn ContentLine::param(self : ContentLine, name : String) -> String?

    The value of the first parameter with this name (TZID, VALUE, ...). Parameter names were normalised to upper case while parsing, and the lookup key is normalised the same way, so callers can pass either spelling.

    PropertyParam

    pub struct PropertyParam {
    name : String
    value : String
    } derive(Eq,
    Debug
    )

    A single parameter of a content line, with surrounding quotes already stripped from its value.

    PropertyParam::equal

    PropertyParam::not_equal

    fn PropertyParam::not_equal(x : PropertyParam, y : PropertyParam) -> Bool

    escape_value

    fn escape_value(text : String) -> String

    Escape text for use as a property value, the inverse of [unescape_value]: a newline becomes \n, and a literal comma, semicolon, or backslash gains its backslash. Characters that need no escape pass through untouched.

    Parsed values keep their escaped wire form, so a round trip through parse and serialize never calls this — it is for building content from plain text.

    Example

    fn test_example() {
    assert_eq(
    @text.escape_value("Team sync, 2026\nRoom B"),
    "Team sync\\, 2026\\nRoom B",
    )
    }

    parse_content_line

    fn parse_content_line(line : String, line_no : Int) -> ContentLine raise

    Parse one logical (already unfolded) content line.

    Raises [ParseError] when there is no value separator or the property name is missing — either way the text is not a usable iCalendar property.

    Example

    fn test_example() raise {
    let line = @text.parse_content_line(
    "DTSTART;TZID=Asia/Shanghai:20260908T093000", 1,
    )
    assert_eq(line.name, "DTSTART")
    assert_eq(line.params[0].value, "Asia/Shanghai")
    assert_eq(line.value, "20260908T093000")
    }

    unescape_value

    fn unescape_value(value : String) -> String

    Turn the RFC 5545 escaped text of a property value into display text.

    \\n and \\N both mean a newline (the latter is common in Apple feeds), \\, and \\; are literal delimiters, \\\\ is a literal backslash. An unknown escape is passed through rather than dropped, so odd feed data still survives a parse.

    Example

    fn test_example() {
    assert_eq(
    @text.unescape_value("Team sync\\, 2026\\nRoom B"),
    "Team sync, 2026\nRoom B",
    )
    }

    unfold

    fn unfold(input : String) -> Array[String]

    Restore RFC 5545 §3.1 folded lines into logical lines.

    A physical line that continues the previous logical line begins with a single WSP (space or horizontal tab); that one WSP is removed and the rest of the text is appended to the previous line. Blank physical lines are dropped, and both CRLF and bare LF are accepted as terminators because real-world feeds are inconsistent about which they emit.

    Example

    fn test_example() {
    let lines = @text.unfold("DESCRIPTION:Line one\r\n more\r\nSUMMARY:Two")
    // The single leading WSP is consumed by unfolding, so "more" joins
    // directly onto "Line one".
    assert_eq(lines, ["DESCRIPTION:Line onemore", "SUMMARY:Two"])
    }