#![allow(dead_code)]
mod utf16;
use self::utf16::escape_sequence;
use std::char;
named!(eat_whitespaces<()>,
do_parse!(
many0!(one_of!(" \t\u{c}")) >>
()
)
);
named!(eat_one_eol<()>,
do_parse!(
alt!(complete!(tag!("\r\n")) | tag!("\r") | tag!("\n")) >>
()
)
);
named!(eat_one_eol_or_eof<()>,
alt!(value!((), eof!()) | eat_one_eol)
);
named!(eat_one_logical_line<()>,
do_parse!(
tag!(r"\") >>
eat_one_eol >>
eat_whitespaces >>
()
)
);
named!(eat_logical_lines<()>,
do_parse!(
many0!(eat_one_logical_line) >>
()
)
);
fn escaped_char_to_char(v: char) -> char {
match v {
't' => '\t',
'n' => '\n',
'f' => '\u{c}',
'r' => '\r',
'\\' => '\\',
_ => v
}
}
named!(escape_in_key_or_value<char>,
do_parse!(
tag!(r"\") >>
c: none_of!("u\r\n") >>
(escaped_char_to_char(c))
)
);
named!(char_in_key<char>,
none_of!(":=\n\r \t\u{c}\\")
);
named!(char_in_value<char>,
none_of!("\n\r\\")
);
named!(one_char_in_key<char>,
alt!(escape_sequence | escape_in_key_or_value | char_in_key)
);
named!(one_char_in_value<char>,
alt!(escape_sequence | escape_in_key_or_value | char_in_value)
);
named!(key<String>,
do_parse!(
chars: sep!(eat_logical_lines, many1!(one_char_in_key)) >>
(chars.into_iter().collect())
)
);
named!(value<String>,
do_parse!(
chars: sep!(eat_logical_lines, many0!(one_char_in_value)) >>
(chars.into_iter().collect())
)
);
named!(eat_whitespaces_and_logical_lines<()>,
do_parse!(
sep!(eat_logical_lines, eat_whitespaces) >>
()
)
);
named!(key_value_line<(String, String)>,
do_parse!(
eat_whitespaces_and_logical_lines >>
k: key >>
eat_whitespaces_and_logical_lines >>
opt!(complete!(one_of!(":="))) >>
eat_whitespaces_and_logical_lines >>
v: value >>
eat_one_eol_or_eof >>
(k, v)
)
);
named!(blank_line<()>,
do_parse!(
eat_whitespaces >>
eat_one_eol_or_eof >>
()
)
);
fn is_eol_char(v: u8) -> bool {
let v = v as char;
v == '\n' || v == '\r'
}
named!(comment_line<()>,
do_parse!(
eat_whitespaces >>
one_of!("#!") >>
take_till!(is_eol_char) >>
eat_one_eol_or_eof >>
()
)
);
named!(full_parser_opt<Vec<Option<(String, String)>>>,
many0!(
alt!(
value!(None, complete!(comment_line)) |
value!(None, complete!(blank_line)) |
opt!(complete!(key_value_line))
)
)
);
named!(pub full_parser<Vec<(String, String)>>,
map!(full_parser_opt, |v| v.into_iter().filter_map(|x| x).collect())
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key() {
assert_done!(key(b"hello"), String::from("hello"));
assert_done_partial!(key(b"hello world"), String::from("hello"), b" world");
assert_done_partial!(key(b"hello:world"), String::from("hello"), b":world");
assert_done_partial!(key(b"hello=world"), String::from("hello"), b"=world");
assert_done_partial!(key(b"hello\nworld"), String::from("hello"), b"\nworld");
assert_done_partial!(key(b"hello\rworld"), String::from("hello"), b"\rworld");
assert_done!(key(b"@#$%^&*()_+-`~?/.>,<|][{};\""), String::from("@#$%^&*()_+-`~?/.>,<|][{};\""));
assert_done!(key(br"key\ with\ spaces"), String::from("key with spaces"));
assert_done!(key(br"key\:with\:colons"), String::from("key:with:colons"));
assert_done!(key(br"key\=with\=equals"), String::from("key=with=equals"));
assert_done!(key(br"now\nwith\rsome\fspecial\tcharacters\\"), String::from("now\nwith\rsome\u{c}special\tcharacters\\"));
assert_done!(key(br"w\iths\omeran\domch\arse\sca\pe\d"), String::from("withsomerandomcharsescaped"));
assert_done!(key(br"\u0048\u0065\u006c\u006c\u006f"), String::from("Hello"));
assert_incomplete!(key(b""));
assert_done!(key(b"abc\\\n def"), String::from("abcdef"));
assert_done!(key(b"gh\\\n \\\r \\\r\nij\\\n\t kl"), String::from("ghijkl"));
assert_done!(key(&[0xA9]), String::from("\u{a9}"));
assert_done_partial!(key(br"abc\uhello"), String::from("abc"), br"\uhello");
}
#[test]
fn test_value() {
assert_done!(value(b"hello"), String::from("hello"));
assert_done!(value(b"h:l=o"), String::from("h:l=o"));
assert_done!(value(b"hello world "), String::from("hello world "));
assert_done!(value(b"/~`!@#$%^&*()-_=+[{]};:'\",<.>/?|"), String::from("/~`!@#$%^&*()-_=+[{]};:'\",<.>/?|"));
assert_done_partial!(value(b"hello\nworld"), String::from("hello"), b"\nworld");
assert_done_partial!(value(b"hello\rworld"), String::from("hello"), b"\rworld");
assert_done!(value(br"now\nwith\rsome\fspecial\tcharacters\\"), String::from("now\nwith\rsome\u{c}special\tcharacters\\"));
assert_done!(value(br"w\iths\omeran\domch\arse\sca\pe\d"), String::from("withsomerandomcharsescaped"));
assert_done!(value(br"\u0048\u0065\u006c\u006c\u006f"), String::from("Hello"));
assert_done!(value(b""), String::from(""));
assert_done!(value(b"abc\\\n def"), String::from("abcdef"));
assert_done!(value(b"gh\\\n \\\r \\\r\nij\\\n\t kl"), String::from("ghijkl"));
assert_done!(value(&[0xA9]), String::from("\u{a9}"));
assert_done_partial!(value(br"abc\uhello"), String::from("abc"), br"\uhello");
}
#[test]
fn test_key_value_line() {
assert_done!(key_value_line(b"hello=world\n"), (String::from("hello"), String::from("world")));
assert_done!(key_value_line(b"hello=world"), (String::from("hello"), String::from("world")));
assert_done!(key_value_line(b"hello =world"), (String::from("hello"), String::from("world")));
assert_done!(key_value_line(b"hello= world"), (String::from("hello"), String::from("world")));
assert_done!(key_value_line(b"hello : world"), (String::from("hello"), String::from("world")));
assert_done!(key_value_line(b"hello world"), (String::from("hello"), String::from("world")));
assert_done!(key_value_line(b" hello = world"), (String::from("hello"), String::from("world")));
assert_error!(key_value_line(b"= world"));
assert_error!(key_value_line(b" = world"));
assert_done!(key_value_line(b"hello=\n"), (String::from("hello"), String::from("")));
assert_done!(key_value_line(b"hello="), (String::from("hello"), String::from("")));
assert_done!(key_value_line(b"hello ="), (String::from("hello"), String::from("")));
assert_done!(key_value_line(b"hello = "), (String::from("hello"), String::from("")));
assert_done!(key_value_line(b" hello ="), (String::from("hello"), String::from("")));
assert_done!(key_value_line(b"hello\n"), (String::from("hello"), String::from("")));
assert_done!(key_value_line(b"hello"), (String::from("hello"), String::from("")));
assert_done!(key_value_line(b"hello "), (String::from("hello"), String::from("")));
assert_done!(key_value_line(br"hello\=cruel=world"), (String::from("hello=cruel"), String::from("world")));
assert_done!(key_value_line(br"hello\==world"), (String::from("hello="), String::from("world")));
assert_done!(key_value_line(br"hello\ cruel = world"), (String::from("hello cruel"), String::from("world")));
assert_done!(key_value_line(br"hello\ = world"), (String::from("hello "), String::from("world")));
assert_done!(key_value_line(br"hello\ncruel = world"), (String::from("hello\ncruel"), String::from("world")));
assert_done!(key_value_line(br"hello\n = world"), (String::from("hello\n"), String::from("world")));
assert_done!(key_value_line(br"hello = \ world"), (String::from("hello"), String::from(" world")));
assert_done!(key_value_line(br"hello\u003dcruel:world"), (String::from("hello=cruel"), String::from("world")));
assert_error!(key_value_line(br"hello\ucruel : world"));
assert_incomplete!(key_value_line(b""));
assert_done!(key_value_line(b"abc\\\n def = 1"), (String::from("abcdef"), String::from("1")));
assert_done!(key_value_line(b"gh\\\n \\\r\n \\\rij\\\n kl = 2"), (String::from("ghijkl"), String::from("2")));
assert_done!(key_value_line(b"mn \\\n = 3"), (String::from("mn"), String::from("3")));
assert_done!(key_value_line(b"op \\\n4"), (String::from("op"), String::from("4")));
assert_done!(key_value_line(b"qrs =\\\n 5"), (String::from("qrs"), String::from("5")));
assert_done!(key_value_line(b"tu = 67\\\n 89"), (String::from("tu"), String::from("6789")));
assert_done!(key_value_line(b"vw 1\\\n2"), (String::from("vw"), String::from("12")));
}
#[test]
fn test_blank() {
assert_done!(blank_line(b"\n"), ());
assert_done!(blank_line(b"\r"), ());
assert_done!(blank_line(b"\r\n"), ());
assert_done!(blank_line(b""), ());
assert_done!(blank_line(b" \t \n"), ());
assert_done!(blank_line(b" \t \r"), ());
assert_done!(blank_line(b" \t \r\n"), ());
assert_done!(blank_line(b" \t "), ());
assert_done_partial!(blank_line(b"\nhello"), (), b"hello");
assert_done_partial!(blank_line(b"\rhello"), (), b"hello");
assert_done_partial!(blank_line(b"\r\nhello"), (), b"hello");
assert_done_partial!(blank_line(b" \t \nhello"), (), b"hello");
assert_done_partial!(blank_line(b" \t \rhello"), (), b"hello");
assert_done_partial!(blank_line(b" \t \r\nhello"), (), b"hello");
}
#[test]
fn test_comment() {
assert_done!(comment_line(b"#hello\n"), ());
assert_done!(comment_line(b"#hello\r"), ());
assert_done!(comment_line(b"#hello\r\n"), ());
assert_done!(comment_line(b"#hello"), ());
assert_done!(comment_line(b"#"), ());
assert_done!(comment_line(b" \t #hello\n"), ());
assert_done!(comment_line(b" \t #hello\r"), ());
assert_done!(comment_line(b" \t #hello\r\n"), ());
assert_done!(comment_line(b" \t #hello"), ());
assert_done!(comment_line(b" \t #"), ());
assert_done!(comment_line(b"!hello\n"), ());
assert_done!(comment_line(b"!hello\r"), ());
assert_done!(comment_line(b"!hello\r\n"), ());
assert_done!(comment_line(b"!"), ());
assert_done!(comment_line(b" \t !hello\n"), ());
assert_done!(comment_line(b" \t !hello\r"), ());
assert_done!(comment_line(b" \t !hello\r\n"), ());
assert_done!(comment_line(b" \t !"), ());
assert_done!(comment_line(b"# \\"), ());
assert_done!(comment_line(b"# \\\n"), ());
assert_done_partial!(comment_line(b"#\nhello"), (), b"hello");
assert_done_partial!(comment_line(b"#\rhello"), (), b"hello");
assert_done_partial!(comment_line(b"#\r\nhello"), (), b"hello");
assert_done_partial!(comment_line(b"# \t \nhello"), (), b"hello");
assert_done_partial!(comment_line(b"# \t \rhello"), (), b"hello");
assert_done_partial!(comment_line(b"# \t \r\nhello"), (), b"hello");
}
fn kv(k: &str, v: &str) -> (String, String) {
(k.to_string(), v.to_string())
}
#[test]
fn test_full_parser() {
assert_done!(full_parser(b""), vec![]);
assert_done!(full_parser(b"# Hello\n"), vec![]);
assert_done!(full_parser(b"# Hello\n! world"), vec![]);
assert_done!(full_parser(b" \t\n \r \t \r\n "), vec![]);
assert_done!(full_parser(b"a = b\nc : d"), vec![kv("a", "b"), kv("c", "d")]);
assert_done!(full_parser(b"a b\nc = d"), vec![kv("a", "b"), kv("c", "d")]);
assert_done!(full_parser(b"# xx\na=b\n\nc:d\r\n \t\r !zz"), vec![kv("a", "b"), kv("c", "d")]);
assert_done!(full_parser(b"#xx\n a =\\\n b\n\n\nc\\\n \\\n : d"), vec![kv("a", "b"), kv("c", "d")]);
}
}