Skip to content

Commit 9c0eaaa

Browse files
committed
Make inline comment separators separate from comment separators
1 parent 1f6f20c commit 9c0eaaa

File tree

2 files changed

+122
-9
lines changed

2 files changed

+122
-9
lines changed

src/ini.rs

+47-9
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub struct Ini {
3232
map: Map<String, Map<String, Option<String>>>,
3333
default_section: std::string::String,
3434
comment_symbols: Vec<char>,
35+
inline_comment_symbols: Option<Vec<char>>,
3536
delimiters: Vec<char>,
3637
boolean_values: HashMap<bool, Vec<String>>,
3738
case_sensitive: bool,
@@ -71,6 +72,17 @@ pub struct IniDefault {
7172
///assert_eq!(default.comment_symbols, vec![';', '#']);
7273
///```
7374
pub comment_symbols: Vec<char>,
75+
///Denotes the set of inline comment symbols for the object. The default of
76+
///`None` means to fall back to the normal comment symbols.
77+
///## Example
78+
///```rust
79+
///use configparser::ini::Ini;
80+
///
81+
///let mut config = Ini::new();
82+
///let default = config.defaults();
83+
///assert_eq!(default.inline_comment_symbols, None);
84+
///```
85+
pub inline_comment_symbols: Option<Vec<char>>,
7486
///Denotes the set delimiters for the key-value pairs.
7587
///## Example
7688
///```rust
@@ -109,6 +121,7 @@ impl Default for IniDefault {
109121
Self {
110122
default_section: "default".to_owned(),
111123
comment_symbols: vec![';', '#'],
124+
inline_comment_symbols: None,
112125
delimiters: vec!['=', ':'],
113126
multiline: false,
114127
boolean_values: [
@@ -285,6 +298,7 @@ impl Ini {
285298
map: Map::new(),
286299
default_section: defaults.default_section,
287300
comment_symbols: defaults.comment_symbols,
301+
inline_comment_symbols: defaults.inline_comment_symbols,
288302
delimiters: defaults.delimiters,
289303
boolean_values: defaults.boolean_values,
290304
case_sensitive: defaults.case_sensitive,
@@ -305,6 +319,7 @@ impl Ini {
305319
IniDefault {
306320
default_section: self.default_section.to_owned(),
307321
comment_symbols: self.comment_symbols.to_owned(),
322+
inline_comment_symbols: self.inline_comment_symbols.to_owned(),
308323
delimiters: self.delimiters.to_owned(),
309324
boolean_values: self.boolean_values.to_owned(),
310325
case_sensitive: self.case_sensitive,
@@ -330,6 +345,7 @@ impl Ini {
330345
pub fn load_defaults(&mut self, defaults: IniDefault) {
331346
self.default_section = defaults.default_section;
332347
self.comment_symbols = defaults.comment_symbols;
348+
self.inline_comment_symbols = defaults.inline_comment_symbols;
333349
self.delimiters = defaults.delimiters;
334350
self.boolean_values = defaults.boolean_values;
335351
self.case_sensitive = defaults.case_sensitive;
@@ -366,6 +382,21 @@ impl Ini {
366382
self.comment_symbols = symlist.to_vec();
367383
}
368384

385+
///Sets the default inline comment symbols to the defined character slice (the default is `None` which falls back to the normal comment symbols).
386+
///Keep in mind that this will remove the default symbols. It must be set before `load()` or `read()` is called in order to take effect.
387+
///## Example
388+
///```rust
389+
///use configparser::ini::Ini;
390+
///
391+
///let mut config = Ini::new();
392+
///config.set_inline_comment_symbols(Some(&['!', '#']));
393+
///let map = config.load("tests/test.ini").unwrap();
394+
///```
395+
///Returns nothing.
396+
pub fn set_inline_comment_symbols(&mut self, symlist: Option<&[char]>) {
397+
self.inline_comment_symbols = symlist.map(|val| val.to_vec());
398+
}
399+
369400
///Sets multiline string support.
370401
///It must be set before `load()` or `read()` is called in order to take effect.
371402
///## Example
@@ -724,6 +755,7 @@ impl Ini {
724755

725756
///Private function that parses ini-style syntax into a Map.
726757
fn parse(&self, input: String) -> Result<Map<String, Map<String, Option<String>>>, String> {
758+
let inline_comment_symbols: &[char] = self.inline_comment_symbols.as_deref().unwrap_or_else(|| self.comment_symbols.as_ref());
727759
let mut map: Map<String, Map<String, Option<String>>> = Map::new();
728760
let mut section = self.default_section.clone();
729761
let mut current_key: Option<String> = None;
@@ -740,23 +772,29 @@ impl Ini {
740772
let mut blank_lines = 0usize;
741773

742774
for (num, raw_line) in input.lines().enumerate() {
775+
let raw_line = raw_line.trim();
776+
777+
// If the line is _just_ a comment, skip it entirely.
743778
let line = match raw_line.find(|c: char| self.comment_symbols.contains(&c)) {
744-
Some(idx) => &raw_line[..idx],
745-
None => raw_line,
779+
Some(0) => continue,
780+
Some(_) | None => raw_line,
746781
};
747782

748-
let trimmed = line.trim();
783+
let line = line.trim();
749784

750785
// Skip empty lines, but keep track of them for multiline values.
751-
if trimmed.is_empty() {
752-
// If a line is _just_ a comment (regardless of whether it's preceded by
753-
// whitespace), ignore it.
754-
if line == raw_line {
755-
blank_lines += 1;
756-
}
786+
if line.is_empty() {
787+
blank_lines += 1;
757788
continue;
758789
}
759790

791+
let line = match line.find(|c: char| inline_comment_symbols.contains(&c)) {
792+
Some(idx) => &raw_line[..idx],
793+
None => raw_line,
794+
};
795+
796+
let trimmed = line.trim();
797+
760798
match (trimmed.find('['), trimmed.rfind(']')) {
761799
(Some(0), Some(end)) => {
762800
section = caser(trimmed[1..end].trim());

tests/test.rs

+75
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,81 @@ basic_option=basic_value
250250
Ok(())
251251
}
252252

253+
#[test]
254+
fn inline_comment_symbols() -> Result<(), Box<dyn Error>> {
255+
const FILE_CONTENTS: &str = "
256+
[basic_section]
257+
; basic comment
258+
; comment with space
259+
! extra_comment=
260+
basic_option=value
261+
basic_with_comment=value ; Simple comment
262+
basic_with_extra_inline=value ! comment
263+
empty_option=
264+
";
265+
266+
let mut config = Ini::new();
267+
config.read(FILE_CONTENTS.to_owned())?;
268+
269+
assert_eq!(
270+
config.get("basic_section", "basic_option"),
271+
Some(String::from("value"))
272+
);
273+
assert_eq!(
274+
config.get("basic_section", "basic_with_comment"),
275+
Some(String::from("value"))
276+
);
277+
assert_eq!(
278+
config.get("basic_section", "basic_with_extra_inline"),
279+
Some(String::from("value ! comment"))
280+
);
281+
assert_eq!(
282+
config.get("basic_section", "! extra_comment"),
283+
Some(String::from(""))
284+
);
285+
286+
assert_eq!(
287+
config.get("basic_section", "empty_option"),
288+
Some(String::from(""))
289+
);
290+
291+
config.set_inline_comment_symbols(Some(&['!']));
292+
293+
config.read(FILE_CONTENTS.to_owned())?;
294+
295+
assert_eq!(
296+
config.get("basic_section", "basic_option"),
297+
Some(String::from("value"))
298+
);
299+
assert_eq!(
300+
config.get("basic_section", "basic_with_comment"),
301+
Some(String::from("value ; Simple comment"))
302+
);
303+
assert_eq!(
304+
config.get("basic_section", "basic_with_extra_inline"),
305+
Some(String::from("value"))
306+
);
307+
308+
config.set_inline_comment_symbols(Some(&[]));
309+
310+
config.read(FILE_CONTENTS.to_owned())?;
311+
312+
assert_eq!(
313+
config.get("basic_section", "basic_option"),
314+
Some(String::from("value"))
315+
);
316+
assert_eq!(
317+
config.get("basic_section", "basic_with_comment"),
318+
Some(String::from("value ; Simple comment"))
319+
);
320+
assert_eq!(
321+
config.get("basic_section", "basic_with_extra_inline"),
322+
Some(String::from("value ! comment"))
323+
);
324+
325+
Ok(())
326+
}
327+
253328
#[test]
254329
#[cfg(feature = "indexmap")]
255330
fn sort_on_write() -> Result<(), Box<dyn Error>> {

0 commit comments

Comments
 (0)