diff --git a/src/lib.rs b/src/lib.rs index 1c2f7fa..5a3daa8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -501,7 +501,7 @@ impl<'a> From> for SectionEntry<'a> { } /// Ini struct -#[derive(Clone, Default)] +#[derive(Clone)] pub struct Ini { sections: ListOrderedMultimap, } @@ -660,6 +660,20 @@ impl Ini { } } +impl Default for Ini { + /// Creates an ini instance with an empty general section. This allows [Ini::general_section] + /// and [Ini::with_general_section] to be called without panicking. + fn default() -> Self { + let mut result = Ini { + sections: Default::default(), + }; + + result.sections.insert(None::, Default::default()); + + result + } +} + impl> Index> for Ini { type Output = Properties; @@ -1344,6 +1358,49 @@ mod test { assert!(!props.contains_key("k1")); } + #[test] + fn load_from_str_with_empty_general_section() { + let input = "[sec1]\nkey1=val1\n"; + let opt = Ini::load_from_str(input); + assert!(opt.is_ok()); + + let mut output = opt.unwrap(); + assert_eq!(output.len(), 2); + + assert!(output.general_section().is_empty()); + assert!(output.general_section_mut().is_empty()); + + let props1 = output.section(None::).unwrap(); + assert!(props1.is_empty()); + let props2 = output.section(Some("sec1")).unwrap(); + assert_eq!(props2.len(), 1); + assert_eq!(props2.get("key1"), Some("val1")); + } + + #[test] + fn load_from_str_with_empty_input() { + let input = ""; + let opt = Ini::load_from_str(input); + assert!(opt.is_ok()); + + let mut output = opt.unwrap(); + assert!(output.general_section().is_empty()); + assert!(output.general_section_mut().is_empty()); + assert_eq!(output.len(), 1); + } + + #[test] + fn load_from_str_with_empty_lines() { + let input = "\n\n\n"; + let opt = Ini::load_from_str(input); + assert!(opt.is_ok()); + + let mut output = opt.unwrap(); + assert!(output.general_section().is_empty()); + assert!(output.general_section_mut().is_empty()); + assert_eq!(output.len(), 1); + } + #[test] #[cfg(not(feature = "brackets-in-section-names"))] fn load_from_str_with_valid_input() { @@ -1352,7 +1409,8 @@ mod test { assert!(opt.is_ok()); let output = opt.unwrap(); - assert_eq!(output.len(), 2); + // there is always a general section + assert_eq!(output.len(), 3); assert!(output.section(Some("sec1")).is_some()); let sec1 = output.section(Some("sec1")).unwrap(); @@ -1844,6 +1902,22 @@ a3 = n3 assert_eq!(keys1, vec!["x2", "xb", "a3"]); } + #[test] + fn write_new() { + use std::str; + + let ini = Ini::new(); + + let opt = WriteOption { + line_separator: LineSeparator::CR, + ..Default::default() + }; + let mut buf = Vec::new(); + ini.write_to_opt(&mut buf, opt).unwrap(); + + assert_eq!("", str::from_utf8(&buf).unwrap()); + } + #[test] fn write_line_separator() { use std::str; @@ -1917,6 +1991,10 @@ bar = f assert_eq!(3, ini.section_all(Some("Peer")).count()); let mut iter = ini.iter(); + // there is always an empty general section + let (k0, p0) = iter.next().unwrap(); + assert_eq!(None, k0); + assert!(p0.is_empty()); let (k1, p1) = iter.next().unwrap(); assert_eq!(Some("Peer"), k1); assert_eq!(Some("a"), p1.get("foo")); @@ -1933,6 +2011,15 @@ bar = f assert_eq!(None, iter.next()); } + #[test] + fn new_has_empty_general_section() { + let mut ini = Ini::new(); + + assert!(ini.general_section().is_empty()); + assert!(ini.general_section_mut().is_empty()); + assert_eq!(ini.len(), 1); + } + #[test] fn fix_issue63() { let section = "PHP";