diff --git a/src/ini.rs b/src/ini.rs index 02127cc..aa2afd0 100755 --- a/src/ini.rs +++ b/src/ini.rs @@ -339,6 +339,57 @@ impl Ini { Ok(self.map.clone()) } + ///Loads a file from a defined path, parses it and applies it to the existing hashmap in our struct. + ///While `load()` will clear the existing `Map`, `load_more()` applies the new values on top of + ///the existing hashmap, preserving previous values. + ///## Example + ///```rust + ///use configparser::ini::Ini; + /// + ///let mut config = Ini::new(); + ///config.load("tests/test.ini").unwrap(); + ///config.load_more("tests/sys_cfg.ini").ok(); // we don't have to worry if this doesn't succeed + ///config.load_more("tests/user_cfg.ini").ok(); // we don't have to worry if this doesn't succeed + ///let map = config.get_map().unwrap(); + /////Then, we can use standard hashmap functions like: + ///let values = map.get("values").unwrap(); + ///``` + ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`. + ///Use `get_mut_map()` if you want a mutable reference. + pub fn load_more>( + &mut self, + path: T, + ) -> Result>>, String> { + let loaded = match self.parse(match fs::read_to_string(&path) { + Err(why) => { + return Err(format!( + "couldn't read {}: {}", + &path.as_ref().display(), + why + )) + } + Ok(s) => s, + }) { + Err(why) => { + return Err(format!( + "couldn't read {}: {}", + &path.as_ref().display(), + why + )) + } + Ok(map) => map, + }; + + for (section, section_map) in loaded.iter() { + self.map + .entry(section.clone()) + .or_insert_with(Map::new) + .extend(section_map.clone()); + } + + Ok(self.map.clone()) + } + ///Reads an input string, parses it and puts the hashmap into our struct. ///At one time, it only stores one configuration, so each call to `load()` or `read()` will clear the existing `Map`, if present. ///## Example @@ -963,6 +1014,48 @@ impl Ini { Ok(self.map.clone()) } + ///Loads a file from a defined path, parses it and applies it to the existing hashmap in our struct. + ///While `load_async()` will clear the existing `Map`, `load_more_async()` applies the new values on top + ///of the existing hashmap, preserving previous values. + /// + ///Usage is similar to `load_more`, but `.await` must be called after along with the usual async rules. + /// + ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`. + ///Use `get_mut_map()` if you want a mutable reference. + pub async fn load_more_async>( + &mut self, + path: T, + ) -> Result>>, String> { + let loaded = match self.parse(match async_fs::read_to_string(&path).await { + Err(why) => { + return Err(format!( + "couldn't read {}: {}", + &path.as_ref().display(), + why + )) + } + Ok(s) => s, + }) { + Err(why) => { + return Err(format!( + "couldn't read {}: {}", + &path.as_ref().display(), + why + )) + } + Ok(map) => map, + }; + + for (section, section_map) in loaded.iter() { + self.map + .entry(section.clone()) + .or_insert_with(Map::new) + .extend(section_map.clone()); + } + + Ok(self.map.clone()) + } + ///Writes the current configuation to the specified path asynchronously. If a file is not present, it is automatically created for you, if a file already ///exists, it is truncated and the configuration is written to it. /// diff --git a/tests/test.rs b/tests/test.rs index b0f473c..54d53fa 100755 --- a/tests/test.rs +++ b/tests/test.rs @@ -100,6 +100,25 @@ fn non_cs() -> Result<(), Box> { mut_map.clear(); config2.clear(); assert_eq!(config.get_map_ref(), config2.get_map_ref()); + + let mut config3 = config.clone(); + let mut_map = config3.get_mut_map(); + mut_map.clear(); + config3.load("tests/test.ini")?; + config3.load_more("tests/test_more.ini")?; + config3.load_more("tests/i_dont_exist.ini").ok(); + assert_eq!( + config3.get("default", "defaultvalues").unwrap(), + "overwritten" + ); + assert_eq!( + config3.get("topsecret", "KFC").unwrap(), + "redacted" + ); + // spacing -> indented exists in tests/test.ini, but not tests/test_more.ini + assert_eq!(config3.get("spacing", "indented").unwrap(), "indented"); + assert_eq!(config3.getbool("values", "Bool")?.unwrap(), false); + Ok(()) } @@ -284,6 +303,27 @@ fn async_load_write() -> Result<(), Box> { Ok(()) } +#[test] +#[cfg(feature = "async-std")] +fn async_load_more() -> Result<(), Box> { + let mut sync_content = Ini::new(); + sync_content.load("tests/test.ini")?; + sync_content.load_more("tests/test_more.ini")?; + sync_content.load_more("tests/i_dont_exist.ini").ok(); + + let async_content = async_std::task::block_on::<_, Result<_, String>>(async { + let mut async_content = Ini::new(); + async_content.load_async("tests/test.ini").await?; + async_content.load_more_async("tests/test_more.ini").await?; + async_content.load_more_async("tests/i_dont_exist.ini").await.ok(); + Ok(async_content) + })?; + + assert_eq!(sync_content, async_content); + + Ok(()) +} + #[test] #[cfg(feature = "indexmap")] fn multiline_off() -> Result<(), Box> { diff --git a/tests/test_more.ini b/tests/test_more.ini new file mode 100755 index 0000000..629eec8 --- /dev/null +++ b/tests/test_more.ini @@ -0,0 +1,8 @@ +defaultvalues=overwritten + +[topsecret] +KFC = redacted + +[values] +Bool = False +