| use super::*; |
| |
| mod section_headers { |
| use winnow::prelude::*; |
| |
| use super::section_header; |
| use crate::parse::tests::util::{fully_consumed, section_header as parsed_section_header}; |
| |
| #[test] |
| fn no_subsection() { |
| assert_eq!( |
| section_header.parse_peek(b"[hello]").unwrap(), |
| fully_consumed(parsed_section_header("hello", None)), |
| ); |
| } |
| |
| #[test] |
| fn modern_subsection() { |
| assert_eq!( |
| section_header.parse_peek(br#"[hello "world"]"#).unwrap(), |
| fully_consumed(parsed_section_header("hello", (" ", "world"))), |
| ); |
| } |
| |
| #[test] |
| fn escaped_subsection() { |
| assert_eq!( |
| section_header.parse_peek(br#"[hello "foo\\bar\""]"#).unwrap(), |
| fully_consumed(parsed_section_header("hello", (" ", r#"foo\bar""#))), |
| ); |
| } |
| |
| #[test] |
| fn deprecated_subsection() { |
| assert_eq!( |
| section_header.parse_peek(br#"[hello.world]"#).unwrap(), |
| fully_consumed(parsed_section_header("hello", (".", "world"))) |
| ); |
| assert_eq!( |
| section_header.parse_peek(br#"[Hello.World]"#).unwrap(), |
| fully_consumed(parsed_section_header("Hello", (".", "World"))) |
| ); |
| } |
| |
| #[test] |
| fn empty_legacy_subsection_name() { |
| assert_eq!( |
| section_header.parse_peek(br#"[hello-world.]"#).unwrap(), |
| fully_consumed(parsed_section_header("hello-world", (".", ""))) |
| ); |
| } |
| |
| #[test] |
| fn empty_modern_subsection_name() { |
| assert_eq!( |
| section_header.parse_peek(br#"[hello ""]"#).unwrap(), |
| fully_consumed(parsed_section_header("hello", (" ", ""))) |
| ); |
| } |
| |
| #[test] |
| fn backslashes_in_subsections_do_not_escape_newlines_or_tabs() { |
| assert_eq!( |
| section_header.parse_peek(br#"[hello "single \ \\ \t \n \0"]"#).unwrap(), |
| fully_consumed(parsed_section_header("hello", (" ", r"single \ t n 0"))) |
| ); |
| } |
| |
| #[test] |
| fn newline_in_header() { |
| assert!(section_header.parse_peek(b"[hello\n]").is_err()); |
| } |
| |
| #[test] |
| fn newline_in_sub_section() { |
| assert!(section_header.parse_peek(b"[hello \"hello\n\"]").is_err()); |
| } |
| |
| #[test] |
| fn null_byt_in_sub_section() { |
| assert!(section_header.parse_peek(b"[hello \"hello\0\"]").is_err()); |
| } |
| |
| #[test] |
| fn escaped_newline_in_sub_section() { |
| assert!(section_header.parse_peek(b"[hello \"hello\\\n\"]").is_err()); |
| } |
| |
| #[test] |
| fn eof_after_escape_in_sub_section() { |
| assert!(section_header.parse_peek(b"[hello \"hello\\").is_err()); |
| } |
| |
| #[test] |
| fn null_byte_in_header() { |
| assert!(section_header.parse_peek(b"[hello\0]").is_err()); |
| } |
| |
| #[test] |
| fn invalid_characters_in_section() { |
| assert!(section_header.parse_peek(b"[$]").is_err()); |
| } |
| #[test] |
| fn invalid_characters_in_legacy_sub_section() { |
| assert!(section_header.parse_peek(b"[hello.$]").is_err()); |
| assert!(section_header.parse_peek(b"[hello. world]").is_err()); |
| } |
| |
| #[test] |
| fn right_brace_in_subsection_name() { |
| assert_eq!( |
| section_header.parse_peek(br#"[hello "]"]"#).unwrap(), |
| fully_consumed(parsed_section_header("hello", (" ", "]"))) |
| ); |
| } |
| } |
| |
| mod sub_section { |
| use std::borrow::Cow; |
| |
| use winnow::prelude::*; |
| |
| use super::sub_section; |
| |
| #[test] |
| fn zero_copy_simple() { |
| let actual = sub_section.parse_peek(b"name\"").unwrap().1; |
| assert_eq!(actual.as_ref(), "name"); |
| assert!(matches!(actual, Cow::Borrowed(_))); |
| } |
| |
| #[test] |
| fn escapes_need_allocation() { |
| let actual = sub_section.parse_peek(br#"\x\t\n\0\\\"""#).unwrap().1; |
| assert_eq!(actual.as_ref(), r#"xtn0\""#); |
| assert!(matches!(actual, Cow::Owned(_))); |
| } |
| } |
| |
| mod config_name { |
| use winnow::prelude::*; |
| |
| use super::config_name; |
| use crate::parse::tests::util::fully_consumed; |
| |
| #[test] |
| fn just_name() { |
| assert_eq!(config_name.parse_peek(b"name").unwrap(), fully_consumed("name".into())); |
| } |
| |
| #[test] |
| fn must_start_with_alphabetic() { |
| assert!(config_name.parse_peek(b"4aaa").is_err()); |
| assert!(config_name.parse_peek(b"-aaa").is_err()); |
| } |
| |
| #[test] |
| fn only_a_subset_of_characters_is_allowed() { |
| assert!(config_name.parse(b"Name$_").is_err()); |
| assert!(config_name.parse(b"other#").is_err()); |
| } |
| |
| #[test] |
| fn cannot_be_empty() { |
| assert!(config_name.parse_peek(b"").is_err()); |
| } |
| } |
| |
| mod section { |
| use crate::parse::{ |
| error::ParseNode, |
| tests::util::{ |
| comment_event, fully_consumed, name_event, newline_custom_event, newline_event, |
| section_header as parsed_section_header, value_done_event, value_event, value_not_done_event, |
| whitespace_event, |
| }, |
| Event, Section, |
| }; |
| |
| fn section<'a>(mut i: &'a [u8], node: &mut ParseNode) -> winnow::IResult<&'a [u8], Section<'a>> { |
| let mut header = None; |
| let mut events = Vec::new(); |
| super::section(&mut i, node, &mut |e| match &header { |
| None => { |
| header = Some(e); |
| } |
| Some(_) => events.push(e), |
| }) |
| .map(|_| { |
| ( |
| i, |
| Section { |
| header: match header.expect("header set") { |
| Event::SectionHeader(header) => header, |
| _ => unreachable!("unexpected"), |
| }, |
| events, |
| }, |
| ) |
| }) |
| } |
| |
| #[test] |
| fn empty_value_with_windows_newlines() { |
| let mut node = ParseNode::SectionHeader; |
| assert_eq!( |
| section(b"[a] k = \r\n", &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("a", None), |
| events: vec![ |
| whitespace_event(" "), |
| name_event("k"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| whitespace_event(" "), |
| value_event(""), |
| newline_custom_event("\r\n") |
| ] |
| }), |
| ); |
| } |
| |
| #[test] |
| fn simple_value_with_windows_newlines() { |
| let mut node = ParseNode::SectionHeader; |
| assert_eq!( |
| section(b"[a] k = v\r\n", &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("a", None), |
| events: vec![ |
| whitespace_event(" "), |
| name_event("k"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| whitespace_event(" "), |
| value_event("v"), |
| newline_custom_event("\r\n") |
| ] |
| }), |
| ); |
| assert_eq!( |
| section(b"[a] k = \r\n", &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("a", None), |
| events: vec![ |
| whitespace_event(" "), |
| name_event("k"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| whitespace_event(" "), |
| value_event(""), |
| newline_custom_event("\r\n") |
| ] |
| }), |
| ); |
| } |
| |
| #[test] |
| fn empty_section() { |
| let mut node = ParseNode::SectionHeader; |
| assert_eq!( |
| section(b"[test]", &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("test", None), |
| events: Default::default() |
| }), |
| ); |
| } |
| |
| #[test] |
| fn simple_section() { |
| let mut node = ParseNode::SectionHeader; |
| let section_data = br#"[hello] |
| a = b |
| c |
| d = "lol""#; |
| assert_eq!( |
| section(section_data, &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("hello", None), |
| events: vec![ |
| newline_event(), |
| whitespace_event(" "), |
| name_event("a"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| whitespace_event(" "), |
| value_event("b"), |
| newline_event(), |
| whitespace_event(" "), |
| name_event("c"), |
| value_event(""), |
| newline_event(), |
| whitespace_event(" "), |
| name_event("d"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| whitespace_event(" "), |
| value_event("\"lol\"") |
| ] |
| }) |
| ); |
| } |
| |
| #[test] |
| fn section_with_empty_value_simplified() { |
| let mut node = ParseNode::SectionHeader; |
| let section_data = b"[a] k="; |
| assert_eq!( |
| section(section_data, &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("a", None), |
| events: vec![ |
| whitespace_event(" "), |
| name_event("k"), |
| Event::KeyValueSeparator, |
| value_event(""), |
| ] |
| }) |
| ); |
| |
| let section_data = b"[a] k=\n"; |
| assert_eq!( |
| section(section_data, &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("a", None), |
| events: vec![ |
| whitespace_event(" "), |
| name_event("k"), |
| Event::KeyValueSeparator, |
| value_event(""), |
| newline_event(), |
| ] |
| }) |
| ); |
| } |
| |
| #[test] |
| fn section_with_empty_value() { |
| let mut node = ParseNode::SectionHeader; |
| let section_data = br#"[hello] |
| a = b |
| c= |
| d = "lol""#; |
| assert_eq!( |
| section(section_data, &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("hello", None), |
| events: vec![ |
| newline_event(), |
| whitespace_event(" "), |
| name_event("a"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| whitespace_event(" "), |
| value_event("b"), |
| newline_event(), |
| whitespace_event(" "), |
| name_event("c"), |
| Event::KeyValueSeparator, |
| value_event(""), |
| newline_event(), |
| whitespace_event(" "), |
| name_event("d"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| whitespace_event(" "), |
| value_event("\"lol\"") |
| ] |
| }) |
| ); |
| } |
| |
| #[test] |
| fn section_implicit_value() { |
| let mut node = ParseNode::SectionHeader; |
| assert_eq!( |
| section(b"[hello] c", &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("hello", None), |
| events: vec![whitespace_event(" "), name_event("c"), value_event("")] |
| }) |
| ); |
| |
| assert_eq!( |
| section(b"[hello] c\nd", &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("hello", None), |
| events: vec![ |
| whitespace_event(" "), |
| name_event("c"), |
| value_event(""), |
| newline_event(), |
| name_event("d"), |
| value_event("") |
| ] |
| }) |
| ); |
| } |
| |
| #[test] |
| fn section_very_commented() { |
| let mut node = ParseNode::SectionHeader; |
| let section_data = br#"[hello] ; commentA |
| a = b # commentB |
| ; commentC |
| ; commentD |
| c = d"#; |
| assert_eq!( |
| section(section_data, &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("hello", None), |
| events: vec![ |
| whitespace_event(" "), |
| comment_event(';', " commentA"), |
| newline_event(), |
| whitespace_event(" "), |
| name_event("a"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| whitespace_event(" "), |
| value_event("b"), |
| whitespace_event(" "), |
| comment_event('#', " commentB"), |
| newline_event(), |
| whitespace_event(" "), |
| comment_event(';', " commentC"), |
| newline_event(), |
| whitespace_event(" "), |
| comment_event(';', " commentD"), |
| newline_event(), |
| whitespace_event(" "), |
| name_event("c"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| whitespace_event(" "), |
| value_event("d"), |
| ] |
| }) |
| ); |
| } |
| |
| #[test] |
| fn complex_continuation() { |
| let mut node = ParseNode::SectionHeader; |
| // This test is absolute hell. Good luck if this fails. |
| assert_eq!( |
| section(b"[section] a = 1 \"\\\"\\\na ; e \"\\\"\\\nd # \"b\t ; c", &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("section", None), |
| events: vec![ |
| whitespace_event(" "), |
| name_event("a"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| whitespace_event(" "), |
| value_not_done_event(r#"1 "\""#), |
| newline_event(), |
| value_not_done_event(r#"a ; e "\""#), |
| newline_event(), |
| value_done_event("d"), |
| whitespace_event(" "), |
| comment_event('#', " \"b\t ; c"), |
| ] |
| }) |
| ); |
| } |
| |
| #[test] |
| fn quote_split_over_two_lines() { |
| let mut node = ParseNode::SectionHeader; |
| assert_eq!( |
| section(b"[section \"a\"] b =\"\\\n;\";a", &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("section", (" ", "a")), |
| events: vec![ |
| whitespace_event(" "), |
| name_event("b"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| value_not_done_event("\""), |
| newline_event(), |
| value_done_event(";\""), |
| comment_event(';', "a"), |
| ] |
| }) |
| ); |
| } |
| |
| #[test] |
| fn section_handles_extraneous_whitespace_before_comment() { |
| let mut node = ParseNode::SectionHeader; |
| assert_eq!( |
| section(b"[s]hello #world", &mut node).unwrap(), |
| fully_consumed(Section { |
| header: parsed_section_header("s", None), |
| events: vec![ |
| name_event("hello"), |
| whitespace_event(" "), |
| value_event(""), |
| comment_event('#', "world"), |
| ] |
| }) |
| ); |
| } |
| } |
| |
| mod value_continuation { |
| use bstr::ByteSlice; |
| |
| use crate::parse::{ |
| tests::util::{newline_custom_event, newline_event, value_done_event, value_not_done_event}, |
| Event, |
| }; |
| |
| pub fn value_impl<'a>(mut i: &'a [u8], events: &mut Vec<Event<'a>>) -> winnow::IResult<&'a [u8], ()> { |
| super::value_impl(&mut i, &mut |e| events.push(e)).map(|_| (i, ())) |
| } |
| |
| #[test] |
| fn simple_continuation() { |
| let mut events = Vec::new(); |
| assert_eq!(value_impl(b"hello\\\nworld", &mut events).unwrap().0, b""); |
| assert_eq!( |
| events, |
| vec![ |
| value_not_done_event("hello"), |
| newline_event(), |
| value_done_event("world") |
| ] |
| ); |
| } |
| |
| #[test] |
| fn continuation_with_whitespace() { |
| let mut events = Vec::new(); |
| assert_eq!(value_impl(b"hello\\\n world", &mut events).unwrap().0, b""); |
| assert_eq!( |
| events, |
| vec![ |
| value_not_done_event("hello"), |
| newline_event(), |
| value_done_event(" world") |
| ] |
| ); |
| |
| let mut events = Vec::new(); |
| assert_eq!(value_impl(b"hello\\\r\n world", &mut events).unwrap().0, b""); |
| assert_eq!( |
| events, |
| vec![ |
| value_not_done_event("hello"), |
| newline_custom_event("\r\n"), |
| value_done_event(" world") |
| ] |
| ); |
| |
| let mut events = Vec::new(); |
| assert!( |
| value_impl(b"hello\\\r\r\n world", &mut events).is_err(), |
| "\\r must be followed by \\n" |
| ); |
| } |
| |
| #[test] |
| fn complex_continuation_with_leftover_comment() { |
| let mut events = Vec::new(); |
| assert_eq!( |
| value_impl(b"1 \"\\\"\\\na ; e \"\\\"\\\nd # \"b\t ; c", &mut events) |
| .unwrap() |
| .0, |
| b" # \"b\t ; c" |
| ); |
| assert_eq!( |
| events, |
| vec![ |
| value_not_done_event(r#"1 "\""#), |
| newline_event(), |
| value_not_done_event(r#"a ; e "\""#), |
| newline_event(), |
| value_done_event("d") |
| ] |
| ); |
| } |
| |
| #[test] |
| fn quote_split_over_two_lines_with_leftover_comment() { |
| let mut events = Vec::new(); |
| assert_eq!(value_impl(b"\"\\\n;\";a", &mut events).unwrap().0, b";a"); |
| assert_eq!( |
| events, |
| vec![value_not_done_event("\""), newline_event(), value_done_event(";\"")] |
| ); |
| |
| let mut events = Vec::new(); |
| assert_eq!(value_impl(b"\"a\\\r\nb;\";c", &mut events).unwrap().0, b";c"); |
| assert_eq!( |
| events, |
| vec![ |
| value_not_done_event("\"a"), |
| newline_custom_event("\r\n"), |
| value_done_event("b;\"") |
| ] |
| ); |
| } |
| |
| #[test] |
| fn quote_split_over_multiple_lines_without_surrounding_quotes_but_inner_quotes() { |
| let mut events = Vec::new(); |
| assert_eq!( |
| value_impl( |
| br#"1\ |
| "2" a\ |
| \"3 b\"\ |
| 4 ; comment "#, |
| &mut events |
| ) |
| .unwrap() |
| .0 |
| .as_bstr(), |
| b" ; comment ".as_bstr() |
| ); |
| assert_eq!( |
| events, |
| vec![ |
| value_not_done_event("1"), |
| newline_event(), |
| value_not_done_event("\"2\" a"), |
| newline_event(), |
| value_not_done_event("\\\"3 b\\\""), |
| newline_event(), |
| value_done_event("4") |
| ] |
| ); |
| } |
| |
| #[test] |
| fn quote_split_over_multiple_lines_with_surrounding_quotes() { |
| let mut events = Vec::new(); |
| assert_eq!( |
| value_impl( |
| br#""1\ |
| "2" a\ |
| \"3 b\"\ |
| 4 " ; comment "#, |
| &mut events |
| ) |
| .unwrap() |
| .0 |
| .as_bstr(), |
| b" ; comment ".as_bstr() |
| ); |
| assert_eq!( |
| events, |
| vec![ |
| value_not_done_event("\"1"), |
| newline_event(), |
| value_not_done_event("\"2\" a"), |
| newline_event(), |
| value_not_done_event("\\\"3 b\\\""), |
| newline_event(), |
| value_done_event("4 \"") |
| ] |
| ); |
| } |
| } |
| |
| mod value_no_continuation { |
| use super::value_continuation::value_impl; |
| use crate::parse::tests::util::value_event; |
| |
| #[test] |
| fn no_comment() { |
| let mut events = Vec::new(); |
| assert_eq!(value_impl(b"hello", &mut events).unwrap().0, b""); |
| assert_eq!(events, vec![value_event("hello")]); |
| } |
| |
| #[test] |
| fn windows_newline() { |
| let mut events = Vec::new(); |
| assert_eq!(value_impl(b"hi\r\nrest", &mut events).unwrap().0, b"\r\nrest"); |
| assert_eq!(events, vec![value_event("hi")]); |
| |
| events.clear(); |
| assert_eq!(value_impl(b"hi\r\r\r\nrest", &mut events).unwrap().0, b"\r\r\r\nrest"); |
| assert_eq!(events, vec![value_event("hi")]); |
| } |
| |
| #[test] |
| fn no_comment_newline() { |
| let mut events = Vec::new(); |
| assert_eq!(value_impl(b"hello\na", &mut events).unwrap().0, b"\na"); |
| assert_eq!(events, vec![value_event("hello")]); |
| } |
| |
| #[test] |
| fn semicolon_comment_not_consumed() { |
| let mut events = Vec::new(); |
| assert_eq!(value_impl(b"hello;world", &mut events).unwrap().0, b";world"); |
| assert_eq!(events, vec![value_event("hello")]); |
| } |
| |
| #[test] |
| fn octothorpe_comment_not_consumed() { |
| let mut events = Vec::new(); |
| assert_eq!(value_impl(b"hello#world", &mut events).unwrap().0, b"#world"); |
| assert_eq!(events, vec![value_event("hello")]); |
| } |
| |
| #[test] |
| fn values_with_extraneous_whitespace_without_comment() { |
| let mut events = Vec::new(); |
| assert_eq!( |
| value_impl(b"hello ", &mut events).unwrap().0, |
| b" " |
| ); |
| assert_eq!(events, vec![value_event("hello")]); |
| } |
| |
| #[test] |
| fn values_with_extraneous_whitespace_before_comment() { |
| let mut events = Vec::new(); |
| assert_eq!( |
| value_impl(b"hello #world", &mut events).unwrap().0, |
| b" #world" |
| ); |
| assert_eq!(events, vec![value_event("hello")]); |
| |
| let mut events = Vec::new(); |
| assert_eq!( |
| value_impl(b"hello ;world", &mut events).unwrap().0, |
| b" ;world" |
| ); |
| assert_eq!(events, vec![value_event("hello")]); |
| } |
| |
| #[test] |
| #[allow(clippy::needless_raw_string_hashes)] |
| fn trans_escaped_comment_marker_not_consumed() { |
| let mut events = Vec::new(); |
| assert_eq!(value_impl(br##"hello"#"world; a"##, &mut events).unwrap().0, b"; a"); |
| assert_eq!(events, vec![value_event(r##"hello"#"world"##)]); |
| } |
| |
| #[test] |
| fn complex_test() { |
| let mut events = Vec::new(); |
| assert_eq!(value_impl(br#"value";";ahhhh"#, &mut events).unwrap().0, b";ahhhh"); |
| assert_eq!(events, vec![value_event(r#"value";""#)]); |
| } |
| |
| #[test] |
| fn garbage_after_continuation_is_err() { |
| assert!(value_impl(b"hello \\afwjdls", &mut Default::default()).is_err()); |
| } |
| |
| #[test] |
| fn invalid_escape() { |
| assert!(value_impl(br"\x", &mut Default::default()).is_err()); |
| } |
| |
| #[test] |
| fn incomplete_quote() { |
| assert!(value_impl(br#"hello "world"#, &mut Default::default()).is_err()); |
| } |
| |
| #[test] |
| fn incomplete_escape() { |
| assert!(value_impl(br"hello world\", &mut Default::default()).is_err()); |
| } |
| } |
| |
| mod key_value_pair { |
| use crate::parse::{ |
| error::ParseNode, |
| tests::util::{name_event, value_event, whitespace_event}, |
| Event, |
| }; |
| |
| fn key_value<'a>( |
| mut i: &'a [u8], |
| node: &mut ParseNode, |
| events: &mut Vec<Event<'a>>, |
| ) -> winnow::IResult<&'a [u8], ()> { |
| super::key_value_pair(&mut i, node, &mut |e| events.push(e)).map(|_| (i, ())) |
| } |
| |
| #[test] |
| fn nonascii_is_allowed_for_values_but_not_for_keys() { |
| let mut node = ParseNode::SectionHeader; |
| let mut vec = Default::default(); |
| assert!( |
| key_value("你好".as_bytes(), &mut node, &mut vec).is_ok(), |
| "Verifying `is_ok` because bad keys get ignored, the caller parser handles this as error" |
| ); |
| assert_eq!(vec, vec![]); |
| |
| let mut node = ParseNode::SectionHeader; |
| let mut vec = Default::default(); |
| assert!(key_value("a = 你好 ".as_bytes(), &mut node, &mut vec).is_ok()); |
| assert_eq!( |
| vec, |
| vec![ |
| name_event("a"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| whitespace_event(" "), |
| value_event("你好") |
| ] |
| ); |
| } |
| |
| #[test] |
| fn whitespace_is_not_ambiguous() { |
| let mut node = ParseNode::SectionHeader; |
| let mut vec = Default::default(); |
| assert!(key_value(b"a =b", &mut node, &mut vec).is_ok()); |
| assert_eq!( |
| vec, |
| vec![ |
| name_event("a"), |
| whitespace_event(" "), |
| Event::KeyValueSeparator, |
| value_event("b") |
| ] |
| ); |
| |
| let mut vec = Default::default(); |
| assert!(key_value(b"a= b", &mut node, &mut vec).is_ok()); |
| assert_eq!( |
| vec, |
| vec![ |
| name_event("a"), |
| Event::KeyValueSeparator, |
| whitespace_event(" "), |
| value_event("b") |
| ] |
| ); |
| } |
| } |
| |
| mod comment { |
| use winnow::prelude::*; |
| |
| use super::comment; |
| use crate::parse::tests::util::{comment as parsed_comment, fully_consumed}; |
| |
| #[test] |
| fn semicolon() { |
| assert_eq!( |
| comment.parse_peek(b"; this is a semicolon comment").unwrap(), |
| fully_consumed(parsed_comment(';', " this is a semicolon comment")), |
| ); |
| } |
| |
| #[test] |
| fn octothorpe() { |
| assert_eq!( |
| comment.parse_peek(b"# this is an octothorpe comment").unwrap(), |
| fully_consumed(parsed_comment('#', " this is an octothorpe comment")), |
| ); |
| } |
| |
| #[test] |
| fn multiple_markers() { |
| assert_eq!( |
| comment.parse_peek(b"###### this is an octothorpe comment").unwrap(), |
| fully_consumed(parsed_comment('#', "##### this is an octothorpe comment")), |
| ); |
| } |
| } |