toml_edit 0.1.1

Yet another format-preserving TOML parser.
Documentation
use combine::*;
use combine::char::{char, crlf, newline as lf};
use combine::range::{recognize, take_while, take_while1};
use combine::primitives::RangeStream;

// wschar = ( %x20 /              ; Space
//            %x09 )              ; Horizontal tab
#[inline]
fn is_wschar(c: char) -> bool {
    match c {
        ' ' | '\t' => true,
        _ => false,
    }
}

// ws = *wschar
parse!(ws() -> &'a str, {
    take_while(is_wschar)
});

// non-eol = %x09 / %x20-10FFFF
#[inline]
fn is_non_eol(c: char) -> bool {
    match c {
        '\u{09}' | '\u{20}'...'\u{10FFFF}' => true,
        _ => false,
    }
}

// comment-start-symbol = %x23 ; #
const COMMENT_START_SYMBOL: char = '#';


// comment = comment-start-symbol *non-eol
parse!(comment() -> &'a str, {
    recognize((
        try(char(COMMENT_START_SYMBOL)),
        take_while(is_non_eol),
    ))
});

// newline = ( %x0A /              ; LF
//             %x0D.0A )           ; CRLF
parse!(newline() -> char, {
    choice((lf(), crlf()))
        .map(|_| '\n')
        .expected("a newline")
});

// ws-newline       = *( wschar / newline )
parse!(ws_newline() -> &'a str, {
    recognize(
        skip_many(choice((
            newline().map(|_| "\n"),
            take_while1(is_wschar),
        ))),
    )
});


// ws-newlines      = newline *( wschar / newline )
parse!(ws_newlines() -> &'a str, {
    recognize((
        newline(),
        ws_newline(),
    ))
});


// note: this rule is not present in the original grammar
// ws-comment-newline = *( ws-newline-nonempty / comment )
parse!(ws_comment_newline() -> &'a str, {
    recognize(
        skip_many(
            choice((
                skip_many1(
                    choice((
                        take_while1(is_wschar),
                        newline().map(|_| "\n"),
                    ))
                ),
                comment().map(|_| ()),
            ))
        )
    )
});

// note: this rule is not present in the original grammar
// line-ending = newline / eof
parse!(line_ending() -> &'a str, {
    choice((
        newline().map(|_| "\n"),
        eof().map(|_| "")
    ))
});

// note: this rule is not present in the original grammar
// line-trailing = ws [comment] skip-line-ending
parse!(line_trailing() -> &'a str, {
    recognize((
        ws(),
        optional(comment()),
    )).skip(line_ending())
});