Skip to content

Commit

Permalink
fix: whitespaces inside quoted values should not be trimeed
Browse files Browse the repository at this point in the history
- fix #140
- ref #138
  • Loading branch information
zonyitoo committed Dec 20, 2024
1 parent fa792ff commit 6385520
Showing 1 changed file with 122 additions and 39 deletions.
161 changes: 122 additions & 39 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1356,8 +1356,7 @@ impl<'a> Parser<'a> {
return self.error("missing key");
}
match self.parse_val() {
Ok(mut mval) => {
mval.trim_in_place();
Ok(mval) => {
match result.entry(cursec.clone()) {
SectionEntry::Vacant(v) => {
// cursec must be None (the General Section)
Expand Down Expand Up @@ -1548,47 +1547,112 @@ impl<'a> Parser<'a> {
// Issue #35: Allow empty value
self.parse_whitespace_except_line_break();

let mut val = match self.ch {
None => Ok(String::new()),
Some('"') if self.opt.enabled_quote => {
self.bump();
self.parse_str_until(&[Some('"')], false).and_then(|s| {
self.bump(); // Eats the last "
// Parse until EOL
self.parse_str_until_eol(cfg!(feature = "inline-comment"))
.map(|x| s + &x)
})
}
Some('\'') if self.opt.enabled_quote => {
self.bump();
self.parse_str_until(&[Some('\'')], false).and_then(|s| {
self.bump(); // Eats the last '
// Parse until EOL
self.parse_str_until_eol(cfg!(feature = "inline-comment"))
.map(|x| s + &x)
})
}
_ => self.parse_str_until_eol(cfg!(feature = "inline-comment")),
}?;
if self.opt.enabled_indented_mutiline_value {
loop {
self.bump();
match self.ch {
Some(' ' | '\t') => {
self.parse_whitespace_except_line_break();
val.push('\n');
let mut new = self.parse_str_until_eol(cfg!(feature = "inline-comment"))?;
new.trim_in_place();
val.push_str(&new)
}
Some('\n') => {
val.push('\n');
continue;
let mut val = String::new();
let mut val_first_part = true;
// Parse the first line of value
'parse_value_line_loop: loop {
match self.ch {
// EOF. Just break
None => break,

// Double Quoted
Some('"') if self.opt.enabled_quote => {
// Bump the current "
self.bump();
// Parse until the next "
let quoted_val = self.parse_str_until(&[Some('"')], false)?;
val.push_str(&quoted_val);

// Eats the "
self.bump();

// characters after " are still part of the value line
val_first_part = false;
continue;
}

// Single Quoted
Some('\'') if self.opt.enabled_quote => {
// Bump the current '
self.bump();
// Parse until the next '
let quoted_val = self.parse_str_until(&[Some('\'')], false)?;
val.push_str(&quoted_val);

// Eats the '
self.bump();

// characters after ' are still part of the value line
val_first_part = false;
continue;
}

// Standard value string
_ => {
// Parse until EOL. White spaces are trimmed (both start and end)
let standard_val = self.parse_str_until_eol(cfg!(feature = "inline-comment"))?;

let trimmed_value = if val_first_part {
// If it is the first part of the value, just trim all of them
standard_val.trim()
} else {
// Otherwise, trim the ends
standard_val.trim_end()
};
val_first_part = false;

val.push_str(trimmed_value);

if self.opt.enabled_indented_mutiline_value {
// Multiline value is supported. We now check whether the next line is started with ' ' or '\t'.
self.bump();

loop {
match self.ch {
Some(' ') | Some('\t') => {
// Multiline value
// Eats the leading spaces
self.parse_whitespace_except_line_break();
// Push a line-break to the current value
val.push('\n');
// continue. Let read the whole value line
continue 'parse_value_line_loop;
}

Some('\r') => {
// Probably \r\n, try to eat one more
self.bump();
if self.ch == Some('\n') {
self.bump();
val.push('\n');
} else {
// \r with a character?
return self.error("\\r is not followed by \\n");
}
}

Some('\n') => {
// New-line, just push and continue
self.bump();
val.push('\n');
}

// Not part of the multiline value
_ => break 'parse_value_line_loop,
}
}
} else {
break;
}
_ => break,
}
}
}

if self.opt.enabled_indented_mutiline_value {
// multiline value, trims line-breaks
val.trim_matches_in_place('\n');
}

Ok(val)
}

Expand Down Expand Up @@ -2798,4 +2862,23 @@ bla = a
}
assert_eq!(bla, "a");
}

#[test]
fn whitespace_inside_quoted_value_should_not_be_trimed() {
let input = r#"
[Foo]
Key=" quoted with whitespace "
"#;

let opt = Ini::load_from_str_opt(
input,
ParseOption {
enabled_quote: true,
..ParseOption::default()
},
)
.unwrap();

assert_eq!(" quoted with whitespace ", opt.get_from(Some("Foo"), "Key").unwrap());
}
}

0 comments on commit 6385520

Please sign in to comment.