Skip to content

Commit

Permalink
trim trailing comments from section header lines
Browse files Browse the repository at this point in the history
Signed-off-by: Kyle Rader <[email protected]>
  • Loading branch information
Kyle Rader committed Mar 1, 2024
1 parent 59b6456 commit 2ced38a
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 23 deletions.
54 changes: 35 additions & 19 deletions src/linereader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,41 @@ type LineReadResult<'a> = Result<Line<'a>, ParseError>;
/// It's usually not necessary to call this function directly.
pub fn parse_line(line: &str) -> LineReadResult<'_> {
let mut l = line.trim_start();
if l.starts_with(|c| c == ';' || c == '#') {
if l.starts_with(is_comment) {
return Ok(Line::Nothing);
}

// check for trailing comments after section headers
let last_closing_bracket = l.rfind(']');
let last_comment = l.rfind(is_comment);

match (last_closing_bracket, last_comment) {
(Some(bracket), Some(comment)) if comment > bracket => {
// there is a comment following a closing bracket, trim it.
l = l[0..comment].as_ref();
}
_ => (),
}

l = l.trim_end();
if l.is_empty() {
Ok(Line::Nothing)
} else {
l = l.trim_end();
if l.is_empty() {
Ok(Line::Nothing)
} else if let Some(s) = l.strip_prefix('[').and_then(|s| s.strip_suffix(']')) {
if s.is_empty() {
Err(ParseError::InvalidLine)
} else {
Ok(Line::Section(s))
}
} else if let Some((key_raw, val_raw)) = l.split_once('=') {
let key = key_raw.trim_end();
let val = val_raw.trim_start();
if key.is_empty() || val.is_empty() {
Err(ParseError::InvalidLine)
} else {
Ok(Line::Pair(key.trim_end(), val.trim_start()))
}
} else if let Some(s) = l.strip_prefix('[').and_then(|s| s.strip_suffix(']')) {
if s.is_empty() {
Err(ParseError::InvalidLine)
} else {
Ok(Line::Section(s))
}
} else if let Some((key_raw, val_raw)) = l.split_once('=') {
let key = key_raw.trim_end();
let val = val_raw.trim_start();
if key.is_empty() || val.is_empty() {
Err(ParseError::InvalidLine)
} else {
Ok(Line::Pair(key.trim_end(), val.trim_start()))
}
} else {
Err(ParseError::InvalidLine)
}
}

Expand Down Expand Up @@ -100,3 +112,7 @@ impl<R: io::BufRead> LineReader<R> {
}
}
}

fn is_comment(c: char) -> bool {
c == ';' || c == '#'
}
16 changes: 13 additions & 3 deletions src/tests/ecparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ fn validate<'a>(
should_be_root: bool,
expected: impl IntoIterator<Item = &'a [(&'a str, &'a str)]>,
) {
let mut parser = crate::ConfigParser::new(text.as_bytes()).unwrap();
let mut parser =
crate::ConfigParser::new(text.as_bytes()).expect("Should have created the parser");
assert_eq!(parser.is_root, should_be_root);
for section_expected in expected {
let section = parser.next().unwrap().unwrap();
let mut iter = section.props().iter().map(|(k, v)| (k, v.into_str()));
for (key, value) in section_expected {
assert_eq!(iter.next(), Some((*key, *value)))
}
assert!(matches!(iter.next(), None));
assert!(iter.next().is_none());
}
assert!(matches!(parser.next(), None));
assert!(parser.next().is_none());
}

macro_rules! expect {
Expand Down Expand Up @@ -71,3 +72,12 @@ fn trailing_newline() {
validate("[foo]\nbar=baz\n", false, expect![[("bar", "baz")]]);
validate("[foo]\nbar=baz\n\n", false, expect![[("bar", "baz")]]);
}

#[test]
fn section_with_comment_after_it() {
validate(
"[/*] # ignore this comment\nk=v",
false,
expect![[("k", "v")]],
);
}
3 changes: 2 additions & 1 deletion src/tests/linereader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ fn valid_sections() {
(" [foo] ", Section("foo")),
("[a=b]", Section("a=b")),
("[#foo]", Section("#foo")),
("[foo] #comment", Section("foo")),
("[foo] ;comment", Section("foo")),
])
}

Expand All @@ -57,7 +59,6 @@ fn valid_nothing() {
fn invalid() {
let lines = [
"[]",
"[section] #comment",
"[close",
"open]",
"][",
Expand Down

0 comments on commit 2ced38a

Please sign in to comment.