Skip to content

Commit 33b8d55

Browse files
committed
Allow for multiline values with escaped newline
1 parent fd0725a commit 33b8d55

File tree

1 file changed

+104
-45
lines changed

1 file changed

+104
-45
lines changed

src/lib.rs

+104-45
Original file line numberDiff line numberDiff line change
@@ -1391,72 +1391,97 @@ impl<'a> Parser<'a> {
13911391
fn parse_str_until(&mut self, endpoint: &[Option<char>], check_inline_comment: bool) -> Result<String, ParseError> {
13921392
let mut result: String = String::new();
13931393

1394+
let mut in_line_continuation = false;
1395+
13941396
while !endpoint.contains(&self.ch) {
13951397
match self.char_or_eof(endpoint)? {
13961398
#[cfg(feature = "inline-comment")]
1397-
space if check_inline_comment && (space == ' ' || space == '\t') => {
1399+
ch if check_inline_comment && (ch == ' ' || ch == '\t') => {
13981400
self.bump();
13991401

14001402
match self.ch {
14011403
Some('#') | Some(';') => {
14021404
// [space]#, [space]; starts an inline comment
1403-
break;
1405+
self.parse_comment();
1406+
if in_line_continuation {
1407+
result.push(ch);
1408+
continue;
1409+
} else {
1410+
break;
1411+
}
14041412
}
14051413
Some(_) => {
1406-
result.push(space);
1414+
result.push(ch);
14071415
continue;
14081416
}
14091417
None => {
1410-
result.push(space);
1418+
result.push(ch);
14111419
}
14121420
}
14131421
}
1414-
'\\' if self.opt.enabled_escape => {
1422+
#[cfg(feature = "inline-comment")]
1423+
ch if check_inline_comment && in_line_continuation && (ch == '#' || ch == ';') => {
1424+
self.parse_comment();
1425+
continue;
1426+
}
1427+
'\\' => {
14151428
self.bump();
1416-
match self.char_or_eof(endpoint)? {
1417-
'0' => result.push('\0'),
1418-
'a' => result.push('\x07'),
1419-
'b' => result.push('\x08'),
1420-
't' => result.push('\t'),
1421-
'r' => result.push('\r'),
1422-
'n' => result.push('\n'),
1423-
'\n' => (),
1424-
'x' => {
1425-
// Unicode 4 character
1426-
let mut code: String = String::with_capacity(4);
1427-
for _ in 0..4 {
1428-
self.bump();
1429-
let ch = self.char_or_eof(endpoint)?;
1430-
if ch == '\\' {
1429+
let Some(ch) = self.ch else {
1430+
result.push('\\');
1431+
continue;
1432+
};
1433+
1434+
if matches!(ch, '\n') {
1435+
in_line_continuation = true;
1436+
} else if self.opt.enabled_escape {
1437+
match ch {
1438+
'0' => result.push('\0'),
1439+
'a' => result.push('\x07'),
1440+
'b' => result.push('\x08'),
1441+
't' => result.push('\t'),
1442+
'r' => result.push('\r'),
1443+
'n' => result.push('\n'),
1444+
'\n' => self.bump(),
1445+
'x' => {
1446+
// Unicode 4 character
1447+
let mut code: String = String::with_capacity(4);
1448+
for _ in 0..4 {
14311449
self.bump();
1432-
if self.ch != Some('\n') {
1433-
return self.error(format!(
1434-
"expecting \"\\\\n\" but \
1450+
let ch = self.char_or_eof(endpoint)?;
1451+
if ch == '\\' {
1452+
self.bump();
1453+
if self.ch != Some('\n') {
1454+
return self.error(format!(
1455+
"expecting \"\\\\n\" but \
14351456
found \"{:?}\".",
1436-
self.ch
1437-
));
1457+
self.ch
1458+
));
1459+
}
14381460
}
1439-
}
14401461

1441-
code.push(ch);
1442-
}
1443-
let r = u32::from_str_radix(&code[..], 16);
1444-
match r.ok().and_then(char::from_u32) {
1445-
Some(ch) => result.push(ch),
1446-
None => return self.error("unknown character in \\xHH form"),
1462+
code.push(ch);
1463+
}
1464+
let r = u32::from_str_radix(&code[..], 16);
1465+
match r.ok().and_then(char::from_u32) {
1466+
Some(ch) => result.push(ch),
1467+
None => return self.error("unknown character in \\xHH form"),
1468+
}
14471469
}
1470+
c => result.push(c),
14481471
}
1449-
c => result.push(c),
1472+
} else {
1473+
result.push('\\');
1474+
result.push(ch);
14501475
}
14511476
}
1452-
ch => {
1453-
result.push(ch);
1454-
}
1477+
ch => result.push(ch),
14551478
}
14561479
self.bump();
14571480
}
14581481

14591482
let _ = check_inline_comment;
1483+
let _ = in_line_continuation;
1484+
14601485
Ok(result)
14611486
}
14621487

@@ -1540,11 +1565,6 @@ impl<'a> Parser<'a> {
15401565
fn parse_str_until_eol(&mut self, check_inline_comment: bool) -> Result<String, ParseError> {
15411566
let r = self.parse_str_until(&[Some('\n'), Some('\r'), None], check_inline_comment)?;
15421567

1543-
#[cfg(feature = "inline-comment")]
1544-
if check_inline_comment && matches!(self.ch, Some('#') | Some(';')) {
1545-
self.parse_comment();
1546-
}
1547-
15481568
Ok(r)
15491569
}
15501570
}
@@ -1828,6 +1848,48 @@ Otherline\"
18281848
assert_eq!(ini.get_from(Some("section name"), "Key").unwrap(), "Value\nOtherline");
18291849
}
18301850

1851+
#[test]
1852+
fn string_multiline_escape() {
1853+
let input = r"
1854+
[section name]
1855+
# This is a comment
1856+
Key = Value \
1857+
Otherline
1858+
";
1859+
let ini = Ini::load_from_str_opt(
1860+
input,
1861+
ParseOption {
1862+
enabled_escape: false,
1863+
..Default::default()
1864+
},
1865+
)
1866+
.unwrap();
1867+
assert_eq!(ini.get_from(Some("section name"), "Key").unwrap(), "Value Otherline");
1868+
}
1869+
1870+
#[cfg(feature = "inline-comment")]
1871+
#[test]
1872+
fn string_multiline_inline_comment() {
1873+
let input = r"
1874+
[section name]
1875+
# This is a comment
1876+
Key = Value \
1877+
# This is also a comment
1878+
; This is also a comment
1879+
# This is also a comment
1880+
Otherline
1881+
";
1882+
let ini = Ini::load_from_str_opt(
1883+
input,
1884+
ParseOption {
1885+
enabled_escape: false,
1886+
..Default::default()
1887+
},
1888+
)
1889+
.unwrap();
1890+
assert_eq!(ini.get_from(Some("section name"), "Key").unwrap(), "Value Otherline");
1891+
}
1892+
18311893
#[test]
18321894
fn string_comment() {
18331895
let input = "
@@ -2035,10 +2097,7 @@ Key = 'Value # This is not a comment ; at all'
20352097
#[test]
20362098
fn load_from_str_noescape() {
20372099
let input = "path=C:\\Windows\\Some\\Folder\\";
2038-
let opt = Ini::load_from_str_noescape(input);
2039-
assert!(opt.is_ok());
2040-
2041-
let output = opt.unwrap();
2100+
let output = Ini::load_from_str_noescape(input).unwrap();
20422101
assert_eq!(output.len(), 1);
20432102
let sec = output.section(None::<String>).unwrap();
20442103
assert_eq!(sec.len(), 1);

0 commit comments

Comments
 (0)