diff --git a/src/ini.rs b/src/ini.rs index b27848a..4796601 100644 --- a/src/ini.rs +++ b/src/ini.rs @@ -674,8 +674,10 @@ impl Ini { for line in lines { out.push_str(LINE_ENDING); - out.push_str(" ".repeat(indent).as_ref()); - out.push_str(line); + if !line.is_empty() { + out.push_str(" ".repeat(indent).as_ref()); + out.push_str(line); + } } } else { out.push_str(value); @@ -734,6 +736,9 @@ impl Ini { } }; + // Track blank lines to preserve them in multiline values. + let mut blank_lines = 0usize; + for (num, raw_line) in input.lines().enumerate() { let line = match raw_line.find(|c: char| self.comment_symbols.contains(&c)) { Some(idx) => &raw_line[..idx], @@ -742,7 +747,13 @@ impl Ini { let trimmed = line.trim(); + // Skip empty lines, but keep track of them for multiline values. if trimmed.is_empty() { + // If a line is _just_ a comment (regardless of whether it's preceded by + // whitespace), ignore it. + if line == raw_line { + blank_lines += 1; + } continue; } @@ -779,41 +790,52 @@ impl Ini { .or_insert_with(|| Some(String::new())); match val { - Some(x) => { - x.push_str(LINE_ENDING); - x.push_str(trimmed); + Some(s) => { + for _ in 0..blank_lines { + s.push_str(LINE_ENDING); + } + s.push_str(LINE_ENDING); + s.push_str(trimmed); } None => { - *val = Some(format!("{}{}", LINE_ENDING, trimmed)); + let mut s = String::with_capacity( + (blank_lines + 1) * LINE_ENDING.len() + trimmed.len(), + ); + for _ in 0..blank_lines { + s.push_str(LINE_ENDING); + } + s.push_str(LINE_ENDING); + s.push_str(trimmed); + *val = Some(s); } } + } else { + let valmap = map.entry(section.clone()).or_default(); - continue; - } + match trimmed.find(&self.delimiters[..]) { + Some(delimiter) => { + let key = caser(trimmed[..delimiter].trim()); - let valmap = map.entry(section.clone()).or_default(); + if key.is_empty() { + return Err(format!("line {}:{}: Key cannot be empty", num, delimiter)); + } else { + current_key = Some(key.clone()); - match trimmed.find(&self.delimiters[..]) { - Some(delimiter) => { - let key = caser(trimmed[..delimiter].trim()); + let value = trimmed[delimiter + 1..].trim().to_owned(); - if key.is_empty() { - return Err(format!("line {}:{}: Key cannot be empty", num, delimiter)); - } else { + valmap.insert(key, Some(value)); + } + } + None => { + let key = caser(trimmed); current_key = Some(key.clone()); - let value = trimmed[delimiter + 1..].trim().to_owned(); - - valmap.insert(key, Some(value)); + valmap.insert(key, None); } } - None => { - let key = caser(trimmed); - current_key = Some(key.clone()); - - valmap.insert(key, None); - } } + + blank_lines = 0; } Ok(map) diff --git a/tests/test.rs b/tests/test.rs index ce71c49..697fb91 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -534,7 +534,7 @@ fn multiline_on() -> Result<(), Box> { assert_eq!(config.get("Section", "Key2").unwrap(), "Value Two"); assert_eq!( config.get("Section", "Key3").unwrap(), - "this is a haiku\nspread across separate lines\na single value" + "this is a haiku\nspread across separate lines\n\na single value" ); assert_eq!(config.get("Section", "Key4").unwrap(), "Four"); @@ -545,6 +545,7 @@ Key1=Value1 Key2=Value Two Key3=this is a haiku spread across separate lines + a single value Key4=Four " diff --git a/tests/test_multiline.ini b/tests/test_multiline.ini index d980e99..0acb35f 100644 --- a/tests/test_multiline.ini +++ b/tests/test_multiline.ini @@ -3,5 +3,7 @@ Key1: Value1 Key2: Value Two Key3: this is a haiku spread across separate lines + # This is a comment + a single value -Key4: Four \ No newline at end of file +Key4: Four