1 //!The ini module provides all the things necessary to load and parse ini-syntax files. The most important of which is the `Ini` struct. 2 //!See the [implementation](https://docs.rs/configparser/*/configparser/ini/struct.Ini.html) documentation for more details. 3 use std::collections::HashMap; 4 use std::convert::AsRef; 5 use std::fs; 6 use std::fs::File; 7 use std::io::prelude::*; 8 use std::path::Path; 9 10 ///The `Ini` struct simply contains a nested hashmap of the loaded configuration, the default section header and comment symbols. 11 ///## Example 12 ///```rust 13 ///use configparser::ini::Ini; 14 /// 15 ///let mut config = Ini::new(); 16 ///``` 17 #[derive(Debug, Clone, Eq, PartialEq, Default)] 18 pub struct Ini { 19 map: HashMap<String, HashMap<String, Option<String>>>, 20 default_section: std::string::String, 21 comment_symbols: Vec<char>, 22 delimiters: Vec<char>, 23 case_sensitive: bool, 24 } 25 26 ///The `IniDefault` struct serves as a template to create other `Ini` objects from. It can be used to store and load 27 ///default properties from different `Ini` objects. 28 ///## Example 29 ///```rust 30 ///use configparser::ini::Ini; 31 /// 32 ///let mut config = Ini::new(); 33 ///let default = config.defaults(); 34 ///let mut config2 = Ini::new_from_defaults(default); // default gets consumed 35 ///``` 36 #[derive(Debug, Clone, Eq, PartialEq, Default)] 37 pub struct IniDefault { 38 ///Denotes the default section header name. 39 ///## Example 40 ///```rust 41 ///use configparser::ini::Ini; 42 /// 43 ///let mut config = Ini::new(); 44 ///let default = config.defaults(); 45 ///assert_eq!(default.default_section, "default"); 46 ///``` 47 pub default_section: std::string::String, 48 ///Denotes the set comment symbols for the object. 49 ///## Example 50 ///```rust 51 ///use configparser::ini::Ini; 52 /// 53 ///let mut config = Ini::new(); 54 ///let default = config.defaults(); 55 ///assert_eq!(default.comment_symbols, vec![';', '#']); 56 ///``` 57 pub comment_symbols: Vec<char>, 58 ///Denotes the set delimiters for the key-value pairs. 59 ///## Example 60 ///```rust 61 ///use configparser::ini::Ini; 62 /// 63 ///let mut config = Ini::new(); 64 ///let default = config.defaults(); 65 ///assert_eq!(default.delimiters, vec!['=', ':']); 66 ///``` 67 pub delimiters: Vec<char>, 68 ///Denotes if the `Ini` object is case-sensitive. 69 ///## Example 70 ///```rust 71 ///use configparser::ini::Ini; 72 /// 73 ///let mut config = Ini::new(); 74 ///let default = config.defaults(); 75 ///assert_eq!(default.case_sensitive, false); 76 ///``` 77 pub case_sensitive: bool, 78 } 79 80 impl Ini { 81 ///Creates a new `HashMap` of `HashMap<String, HashMap<String, Option<String>>>` type for the struct. 82 ///All values in the HashMap are stored in `String` type. 83 ///## Example 84 ///```rust 85 ///use configparser::ini::Ini; 86 /// 87 ///let mut config = Ini::new(); 88 ///``` 89 ///Returns the struct and stores it in the calling variable. new() -> Ini90 pub fn new() -> Ini { 91 Ini { 92 map: HashMap::new(), 93 default_section: "default".to_owned(), 94 comment_symbols: vec![';', '#'], 95 delimiters: vec!['=', ':'], 96 case_sensitive: false, 97 } 98 } 99 100 ///Creates a new **case-sensitive** `HashMap` of `HashMap<String, HashMap<String, Option<String>>>` type for the struct. 101 ///All values in the HashMap are stored in `String` type. 102 ///## Example 103 ///```rust 104 ///use configparser::ini::Ini; 105 /// 106 ///let mut config = Ini::new_cs(); 107 ///``` 108 ///Returns the struct and stores it in the calling variable. new_cs() -> Ini109 pub fn new_cs() -> Ini { 110 Ini { 111 map: HashMap::new(), 112 default_section: "default".to_owned(), 113 comment_symbols: vec![';', '#'], 114 delimiters: vec!['=', ':'], 115 case_sensitive: true, 116 } 117 } 118 119 ///Creates a new `Ini` with the given defaults from an existing `IniDefault` object. 120 ///## Example 121 ///```rust 122 ///use configparser::ini::Ini; 123 ///use configparser::ini::IniDefault; 124 /// 125 ///let default = IniDefault { 126 /// default_section: "default".to_owned(), 127 /// comment_symbols: vec![';'], 128 /// delimiters: vec!['='], 129 /// case_sensitive: true, 130 ///}; 131 ///let mut config = Ini::new_from_defaults(default.clone()); 132 ///// Now, load as usual with new defaults: 133 ///let map = config.load("tests/test.ini").unwrap(); 134 ///assert_eq!(config.defaults(), default); 135 /// 136 ///``` new_from_defaults(defaults: IniDefault) -> Ini137 pub fn new_from_defaults(defaults: IniDefault) -> Ini { 138 Ini { 139 map: HashMap::new(), 140 default_section: defaults.default_section, 141 comment_symbols: defaults.comment_symbols, 142 delimiters: defaults.delimiters, 143 case_sensitive: defaults.case_sensitive, 144 } 145 } 146 147 ///Fetches the defaults from the current `Ini` object and stores it as a `IniDefault` struct for usage elsewhere. 148 ///## Example 149 ///```rust 150 ///use configparser::ini::Ini; 151 /// 152 ///let mut config = Ini::new(); 153 ///let default = config.defaults(); 154 ///``` 155 ///Returns an `IniDefault` object. Keep in mind that it will get borrowed since it has non-`Copy` types. defaults(&self) -> IniDefault156 pub fn defaults(&self) -> IniDefault { 157 IniDefault { 158 default_section: self.default_section.to_owned(), 159 comment_symbols: self.comment_symbols.to_owned(), 160 delimiters: self.delimiters.to_owned(), 161 case_sensitive: self.case_sensitive, 162 } 163 } 164 165 ///Takes an `IniDefault` object and stores its properties in the calling `Ini` object. This happens in-place and 166 ///does not work retroactively, only future operations are affected. 167 ///## Example 168 ///```rust 169 ///use configparser::ini::Ini; 170 ///use configparser::ini::IniDefault; 171 /// 172 ///let mut config = Ini::new(); 173 ///let default = IniDefault { 174 /// default_section: "default".to_owned(), 175 /// comment_symbols: vec![';', '#'], 176 /// delimiters: vec!['=', ':'], 177 /// case_sensitive: true, 178 ///}; // This is equivalent to ini_cs() defaults 179 ///config.load_defaults(default.clone()); 180 ///assert_eq!(config.defaults(), default); 181 ///``` 182 ///Returns nothing. load_defaults(&mut self, defaults: IniDefault)183 pub fn load_defaults(&mut self, defaults: IniDefault) { 184 self.default_section = defaults.default_section; 185 self.comment_symbols = defaults.comment_symbols; 186 self.delimiters = defaults.delimiters; 187 self.case_sensitive = defaults.case_sensitive; 188 } 189 190 ///Sets the default section header to the defined string (the default is `default`). 191 ///It must be set before `load()` or `read()` is called in order to take effect. 192 ///## Example 193 ///```rust 194 ///use configparser::ini::Ini; 195 /// 196 ///let mut config = Ini::new(); 197 /// 198 ///config.set_default_section("topsecret"); 199 ///let map = config.load("tests/test.ini").unwrap(); 200 ///``` 201 ///Returns nothing. set_default_section(&mut self, section: &str)202 pub fn set_default_section(&mut self, section: &str) { 203 self.default_section = section.to_owned(); 204 } 205 206 ///Sets the default comment symbols to the defined character slice (the defaults are `;` and `#`). 207 ///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. 208 ///## Example 209 ///```rust 210 ///use configparser::ini::Ini; 211 /// 212 ///let mut config = Ini::new(); 213 ///config.set_comment_symbols(&['!', '#']); 214 ///let map = config.load("tests/test.ini").unwrap(); 215 ///``` 216 ///Returns nothing. set_comment_symbols(&mut self, symlist: &[char])217 pub fn set_comment_symbols(&mut self, symlist: &[char]) { 218 self.comment_symbols = symlist.to_vec(); 219 } 220 221 ///Gets all the sections of the currently-stored `HashMap` in a vector. 222 ///## Example 223 ///```rust 224 ///use configparser::ini::Ini; 225 /// 226 ///let mut config = Ini::new(); 227 ///config.load("tests/test.ini"); 228 ///let sections = config.sections(); 229 ///``` 230 ///Returns `Vec<String>`. sections(&self) -> Vec<String>231 pub fn sections(&self) -> Vec<String> { 232 self.map.keys().cloned().collect() 233 } 234 235 ///Loads a file from a defined path, parses it and puts the hashmap into our struct. 236 ///At one time, it only stores one configuration, so each call to `load()` or `read()` will clear the existing `HashMap`, if present. 237 ///## Example 238 ///```rust 239 ///use configparser::ini::Ini; 240 /// 241 ///let mut config = Ini::new(); 242 ///let map = config.load("tests/test.ini").unwrap(); // we can get a clone like this, or just store it 243 /////Then, we can use standard hashmap functions like: 244 ///let values = map.get("values").unwrap(); 245 ///``` 246 ///Returns `Ok(map)` with a clone of the stored `HashMap` if no errors are thrown or else `Err(error_string)`. 247 ///Use `get_mut_map()` if you want a mutable reference. load<T: AsRef<Path>>( &mut self, path: T, ) -> Result<HashMap<String, HashMap<String, Option<String>>>, String>248 pub fn load<T: AsRef<Path>>( 249 &mut self, 250 path: T, 251 ) -> Result<HashMap<String, HashMap<String, Option<String>>>, String> { 252 let path = path.as_ref(); 253 let display = path.display(); 254 255 let mut file = match File::open(&path) { 256 Err(why) => return Err(format!("couldn't open {}: {}", display, why)), 257 Ok(file) => file, 258 }; 259 260 let mut s = String::new(); 261 self.map = match file.read_to_string(&mut s) { 262 Err(why) => return Err(format!("couldn't read {}: {}", display, why)), 263 Ok(_) => match self.parse(s) { 264 Err(why) => return Err(why), 265 Ok(map) => map, 266 }, 267 }; 268 Ok(self.map.clone()) 269 } 270 271 ///Reads an input string, parses it and puts the hashmap into our struct. 272 ///At one time, it only stores one configuration, so each call to `load()` or `read()` will clear the existing `HashMap`, if present. 273 ///## Example 274 ///```rust 275 ///use configparser::ini::Ini; 276 /// 277 ///let mut config = Ini::new(); 278 ///let map = match config.read(String::from( 279 /// "[2000s] 280 /// 2020 = bad")) { 281 /// Err(why) => panic!("{}", why), 282 /// Ok(inner) => inner 283 ///}; 284 ///let this_year = map["2000s"]["2020"].clone().unwrap(); 285 ///assert_eq!(this_year, "bad"); // value accessible! 286 ///``` 287 ///Returns `Ok(map)` with a clone of the stored `HashMap` if no errors are thrown or else `Err(error_string)`. 288 ///Use `get_mut_map()` if you want a mutable reference. read( &mut self, input: String, ) -> Result<HashMap<String, HashMap<String, Option<String>>>, String>289 pub fn read( 290 &mut self, 291 input: String, 292 ) -> Result<HashMap<String, HashMap<String, Option<String>>>, String> { 293 self.map = match self.parse(input) { 294 Err(why) => return Err(why), 295 Ok(map) => map, 296 }; 297 Ok(self.map.clone()) 298 } 299 300 ///Writes the current configuation to the specified path. If a file is not present, it is automatically created for you, if a file already 301 ///exists, it is truncated and the configuration is written to it. 302 ///## Example 303 ///```rust 304 ///use configparser::ini::Ini; 305 /// 306 ///fn main() -> std::io::Result<()> { 307 /// let mut config = Ini::new(); 308 /// config.read(String::from( 309 /// "[2000s] 310 /// 2020 = bad")); 311 /// config.write("output.ini") 312 ///} 313 ///``` 314 ///Returns a `std::io::Result<()>` type dependent on whether the write was successful or not. write<T: AsRef<Path>>(&self, path: T) -> std::io::Result<()>315 pub fn write<T: AsRef<Path>>(&self, path: T) -> std::io::Result<()> { 316 fs::write(path.as_ref(), self.unparse()) 317 } 318 319 ///Returns a string with the current configuration formatted with valid ini-syntax. This is always safe since the configuration is validated during 320 ///parsing. 321 ///## Example 322 ///```rust 323 ///use configparser::ini::Ini; 324 /// 325 ///let mut config = Ini::new(); 326 ///config.read(String::from( 327 /// "[2000s] 328 /// 2020 = bad")); 329 ///let outstring = config.writes(); 330 ///``` 331 ///Returns a `String` type contatining the ini-syntax file. writes(&self) -> String332 pub fn writes(&self) -> String { 333 self.unparse() 334 } 335 336 ///Private function that converts the currently stored configuration into a valid ini-syntax string. unparse(&self) -> String337 fn unparse(&self) -> String { 338 // push key/value pairs in outmap to out string. 339 fn unparse_key_values(out: &mut String, outmap: &HashMap<String, Option<String>>) { 340 for (key, val) in outmap.iter() { 341 out.push_str(&key); 342 if let Some(value) = val { 343 out.push('='); 344 out.push_str(&value); 345 } 346 out.push('\n'); 347 } 348 } 349 let mut out = String::new(); 350 let mut cloned = self.map.clone(); 351 if let Some(defaultmap) = cloned.get(&self.default_section) { 352 unparse_key_values(&mut out, defaultmap); 353 cloned.remove(&self.default_section); 354 } 355 for (section, secmap) in cloned.iter() { 356 out.push_str(&format!("[{}]", section)); 357 out.push('\n'); 358 unparse_key_values(&mut out, secmap); 359 } 360 out 361 } 362 363 ///Private function that parses ini-style syntax into a HashMap. parse( &self, input: String, ) -> Result<HashMap<String, HashMap<String, Option<String>>>, String>364 fn parse( 365 &self, 366 input: String, 367 ) -> Result<HashMap<String, HashMap<String, Option<String>>>, String> { 368 let mut map: HashMap<String, HashMap<String, Option<String>>> = HashMap::new(); 369 let mut section = self.default_section.clone(); 370 let caser = |val: &str| { 371 if self.case_sensitive { 372 val.to_owned() 373 } else { 374 val.to_lowercase() 375 } 376 }; 377 for (num, lines) in input.lines().enumerate() { 378 let trimmed = match lines.find(|c: char| self.comment_symbols.contains(&c)) { 379 Some(idx) => lines[..idx].trim(), 380 None => lines.trim(), 381 }; 382 if trimmed.is_empty() { 383 continue; 384 } 385 match trimmed.find('[') { 386 Some(0) => match trimmed.rfind(']') { 387 Some(end) => { 388 section = caser(trimmed[1..end].trim()); 389 } 390 None => { 391 return Err(format!( 392 "line {}: Found opening bracket for section name but no closing bracket", 393 num 394 )); 395 } 396 }, 397 Some(_) | None => match trimmed.find(&self.delimiters[..]) { 398 Some(delimiter) => match map.get_mut(§ion) { 399 Some(valmap) => { 400 let key = caser(trimmed[..delimiter].trim()); 401 let value = trimmed[delimiter + 1..].trim().to_owned(); 402 if key.is_empty() { 403 return Err(format!( 404 "line {}:{}: Key cannot be empty", 405 num, delimiter 406 )); 407 } else { 408 valmap.insert(key, Some(value)); 409 } 410 } 411 None => { 412 let mut valmap: HashMap<String, Option<String>> = HashMap::new(); 413 let key = caser(trimmed[..delimiter].trim()); 414 let value = trimmed[delimiter + 1..].trim().to_owned(); 415 if key.is_empty() { 416 return Err(format!( 417 "line {}:{}: Key cannot be empty", 418 num, delimiter 419 )); 420 } else { 421 valmap.insert(key, Some(value)); 422 } 423 map.insert(section.clone(), valmap); 424 } 425 }, 426 None => match map.get_mut(§ion) { 427 Some(valmap) => { 428 let key = caser(trimmed); 429 valmap.insert(key, None); 430 } 431 None => { 432 let mut valmap: HashMap<String, Option<String>> = HashMap::new(); 433 let key = caser(trimmed); 434 valmap.insert(key, None); 435 map.insert(section.clone(), valmap); 436 } 437 }, 438 }, 439 } 440 } 441 Ok(map) 442 } 443 444 ///Private function that cases things automatically depending on the set variable. autocase(&self, section: &str, key: &str) -> (String, String)445 fn autocase(&self, section: &str, key: &str) -> (String, String) { 446 if self.case_sensitive { 447 (section.to_owned(), key.to_owned()) 448 } else { 449 (section.to_lowercase(), key.to_lowercase()) 450 } 451 } 452 453 ///Returns a clone of the stored value from the key stored in the defined section. 454 ///Unlike accessing the map directly, `get()` can process your input to make case-insensitive access *if* the 455 ///default constructor is used. 456 ///All `get` functions will do this automatically under the hood. 457 ///## Example 458 ///```rust 459 ///use configparser::ini::Ini; 460 /// 461 ///let mut config = Ini::new(); 462 ///config.load("tests/test.ini"); 463 ///let value = config.get("default", "defaultvalues").unwrap(); 464 ///assert_eq!(value, String::from("defaultvalues")); 465 ///``` 466 ///Returns `Some(value)` of type `String` if value is found or else returns `None`. get(&self, section: &str, key: &str) -> Option<String>467 pub fn get(&self, section: &str, key: &str) -> Option<String> { 468 let (section, key) = self.autocase(section, key); 469 self.map.get(§ion)?.get(&key)?.clone() 470 } 471 472 ///Parses the stored value from the key stored in the defined section to a `bool`. 473 ///For ease of use, the function converts the type case-insensitively (`true` == `True`). 474 ///## Example 475 ///```rust 476 ///use configparser::ini::Ini; 477 /// 478 ///let mut config = Ini::new(); 479 ///config.load("tests/test.ini"); 480 ///let value = config.getbool("values", "bool").unwrap().unwrap(); 481 ///assert!(value); // value accessible! 482 ///``` 483 ///Returns `Ok(Some(value))` of type `bool` if value is found or else returns `Ok(None)`. 484 ///If the parsing fails, it returns an `Err(string)`. getbool(&self, section: &str, key: &str) -> Result<Option<bool>, String>485 pub fn getbool(&self, section: &str, key: &str) -> Result<Option<bool>, String> { 486 let (section, key) = self.autocase(section, key); 487 match self.map.get(§ion) { 488 Some(secmap) => match secmap.get(&key) { 489 Some(val) => match val { 490 Some(inner) => match inner.to_lowercase().parse::<bool>() { 491 Err(why) => Err(why.to_string()), 492 Ok(boolean) => Ok(Some(boolean)), 493 }, 494 None => Ok(None), 495 }, 496 None => Ok(None), 497 }, 498 None => Ok(None), 499 } 500 } 501 502 ///Parses the stored value from the key stored in the defined section to a `bool`. For ease of use, the function converts the type coerces a match. 503 ///It attempts to case-insenstively find `true`, `yes`, `t`, `y`, `1` and `on` to parse it as `True`. 504 ///Similarly it attempts to case-insensitvely find `false`, `no`, `f`, `n`, `0` and `off` to parse it as `False`. 505 ///## Example 506 ///```rust 507 ///use configparser::ini::Ini; 508 /// 509 ///let mut config = Ini::new(); 510 ///config.load("tests/test.ini"); 511 ///let value = config.getboolcoerce("values", "boolcoerce").unwrap().unwrap(); 512 ///assert!(!value); // value accessible! 513 ///``` 514 ///Returns `Ok(Some(value))` of type `bool` if value is found or else returns `Ok(None)`. 515 ///If the parsing fails, it returns an `Err(string)`. getboolcoerce(&self, section: &str, key: &str) -> Result<Option<bool>, String>516 pub fn getboolcoerce(&self, section: &str, key: &str) -> Result<Option<bool>, String> { 517 let (section, key) = self.autocase(section, key); 518 match self.map.get(§ion) { 519 Some(secmap) => match secmap.get(&key) { 520 Some(val) => match val { 521 Some(inner) => { 522 let boolval = &inner.to_lowercase()[..]; 523 if ["true", "yes", "t", "y", "1", "on"].contains(&boolval) { 524 Ok(Some(true)) 525 } else if ["false", "no", "f", "n", "0", "off"].contains(&boolval) { 526 Ok(Some(false)) 527 } else { 528 Err(format!( 529 "Unable to parse value into bool at {}:{}", 530 section, key 531 )) 532 } 533 } 534 None => Ok(None), 535 }, 536 None => Ok(None), 537 }, 538 None => Ok(None), 539 } 540 } 541 542 ///Parses the stored value from the key stored in the defined section to an `i64`. 543 ///## Example 544 ///```rust 545 ///use configparser::ini::Ini; 546 /// 547 ///let mut config = Ini::new(); 548 ///config.load("tests/test.ini"); 549 ///let value = config.getint("values", "int").unwrap().unwrap(); 550 ///assert_eq!(value, -31415); // value accessible! 551 ///``` 552 ///Returns `Ok(Some(value))` of type `i64` if value is found or else returns `Ok(None)`. 553 ///If the parsing fails, it returns an `Err(string)`. getint(&self, section: &str, key: &str) -> Result<Option<i64>, String>554 pub fn getint(&self, section: &str, key: &str) -> Result<Option<i64>, String> { 555 let (section, key) = self.autocase(section, key); 556 match self.map.get(§ion) { 557 Some(secmap) => match secmap.get(&key) { 558 Some(val) => match val { 559 Some(inner) => match inner.parse::<i64>() { 560 Err(why) => Err(why.to_string()), 561 Ok(int) => Ok(Some(int)), 562 }, 563 None => Ok(None), 564 }, 565 None => Ok(None), 566 }, 567 None => Ok(None), 568 } 569 } 570 571 ///Parses the stored value from the key stored in the defined section to a `u64`. 572 ///## Example 573 ///```rust 574 ///use configparser::ini::Ini; 575 /// 576 ///let mut config = Ini::new(); 577 ///config.load("tests/test.ini"); 578 ///let value = config.getint("values", "Uint").unwrap().unwrap(); 579 ///assert_eq!(value, 31415); // value accessible! 580 ///``` 581 ///Returns `Ok(Some(value))` of type `u64` if value is found or else returns `Ok(None)`. 582 ///If the parsing fails, it returns an `Err(string)`. getuint(&self, section: &str, key: &str) -> Result<Option<u64>, String>583 pub fn getuint(&self, section: &str, key: &str) -> Result<Option<u64>, String> { 584 let (section, key) = self.autocase(section, key); 585 match self.map.get(§ion) { 586 Some(secmap) => match secmap.get(&key) { 587 Some(val) => match val { 588 Some(inner) => match inner.parse::<u64>() { 589 Err(why) => Err(why.to_string()), 590 Ok(uint) => Ok(Some(uint)), 591 }, 592 None => Ok(None), 593 }, 594 None => Ok(None), 595 }, 596 None => Ok(None), 597 } 598 } 599 600 ///Parses the stored value from the key stored in the defined section to a `f64`. 601 ///## Example 602 ///```rust 603 ///use configparser::ini::Ini; 604 /// 605 ///let mut config = Ini::new(); 606 ///config.load("tests/test.ini"); 607 ///let value = config.getfloat("values", "float").unwrap().unwrap(); 608 ///assert_eq!(value, 3.1415); // value accessible! 609 ///``` 610 ///Returns `Ok(Some(value))` of type `f64` if value is found or else returns `Ok(None)`. 611 ///If the parsing fails, it returns an `Err(string)`. getfloat(&self, section: &str, key: &str) -> Result<Option<f64>, String>612 pub fn getfloat(&self, section: &str, key: &str) -> Result<Option<f64>, String> { 613 let (section, key) = self.autocase(section, key); 614 match self.map.get(§ion) { 615 Some(secmap) => match secmap.get(&key) { 616 Some(val) => match val { 617 Some(inner) => match inner.parse::<f64>() { 618 Err(why) => Err(why.to_string()), 619 Ok(float) => Ok(Some(float)), 620 }, 621 None => Ok(None), 622 }, 623 None => Ok(None), 624 }, 625 None => Ok(None), 626 } 627 } 628 629 ///Returns a clone of the `HashMap` stored in our struct. 630 ///## Example 631 ///```rust 632 ///use configparser::ini::Ini; 633 /// 634 ///let mut config = Ini::new(); 635 ///config.read(String::from( 636 /// "[section] 637 /// key=values")); 638 ///let map = config.get_map().unwrap(); 639 ///assert_eq!(map, *config.get_map_ref()); // the cloned map is basically a snapshot that you own 640 ///``` 641 ///Returns `Some(map)` if map is non-empty or else returns `None`. 642 ///Similar to `load()` but returns an `Option` type with the currently stored `HashMap`. get_map(&self) -> Option<HashMap<String, HashMap<String, Option<String>>>>643 pub fn get_map(&self) -> Option<HashMap<String, HashMap<String, Option<String>>>> { 644 if self.map.is_empty() { 645 None 646 } else { 647 Some(self.map.clone()) 648 } 649 } 650 651 ///Returns an immutable reference to the `HashMap` stored in our struct. 652 ///## Example 653 ///```rust 654 ///use configparser::ini::Ini; 655 /// 656 ///let mut config = Ini::new(); 657 ///let mapclone = config.read(String::from 658 /// ("[topsecrets] 659 /// Valueless key")).unwrap(); 660 /////Think of the clone as being a snapshot at a point of time while the reference always points to the current configuration. 661 ///assert_eq!(*config.get_map_ref(), mapclone); // same as expected. 662 ///``` 663 ///If you just need to definitely mutate the map, use `get_mut_map()` instead. Alternatively, you can generate a snapshot by getting a clone 664 ///with `get_map()` and work with that. get_map_ref(&self) -> &HashMap<String, HashMap<String, Option<String>>>665 pub fn get_map_ref(&self) -> &HashMap<String, HashMap<String, Option<String>>> { 666 &self.map 667 } 668 669 ///Returns a mutable reference to the `HashMap` stored in our struct. 670 ///## Example 671 ///```rust 672 ///use configparser::ini::Ini; 673 /// 674 ///let mut config = Ini::new(); 675 ///config.read(String::from 676 /// ("[topsecrets] 677 /// Valueless key")); 678 /////We can then get the mutable map and insert a value like: 679 ///config.get_mut_map().get_mut("topsecrets").unwrap().insert(String::from("nuclear launch codes"), None); 680 ///assert_eq!(config.get("topsecrets", "nuclear launch codes"), None); // inserted successfully! 681 ///``` 682 ///If you just need to access the map without mutating, use `get_map_ref()` or make a clone with `get_map()` instead. get_mut_map(&mut self) -> &mut HashMap<String, HashMap<String, Option<String>>>683 pub fn get_mut_map(&mut self) -> &mut HashMap<String, HashMap<String, Option<String>>> { 684 &mut self.map 685 } 686 687 ///Sets an `Option<String>` in the `HashMap` stored in our struct. If a particular section or key does not exist, it will be automatically created. 688 ///An existing value in the map will be overwritten. You can also set `None` safely. 689 ///## Example 690 ///```rust 691 ///use configparser::ini::Ini; 692 /// 693 ///let mut config = Ini::new(); 694 ///config.read(String::from( 695 /// "[section] 696 /// key=value")); 697 ///let key_value = String::from("value"); 698 ///config.set("section", "key", Some(key_value)); 699 ///config.set("section", "key", None); // also valid! 700 ///assert_eq!(config.get("section", "key"), None); // correct! 701 ///``` 702 ///Returns `None` if there is no existing value, else returns `Some(Option<String>)`, with the existing value being the wrapped `Option<String>`. 703 ///If you want to insert using a string literal, use `setstr()` instead. set( &mut self, section: &str, key: &str, value: Option<String>, ) -> Option<Option<String>>704 pub fn set( 705 &mut self, 706 section: &str, 707 key: &str, 708 value: Option<String>, 709 ) -> Option<Option<String>> { 710 let (section, key) = self.autocase(section, key); 711 match self.map.get_mut(§ion) { 712 Some(secmap) => secmap.insert(key, value), 713 None => { 714 let mut valmap: HashMap<String, Option<String>> = HashMap::new(); 715 valmap.insert(key, value); 716 self.map.insert(section, valmap); 717 None 718 } 719 } 720 } 721 722 ///Sets an `Option<&str>` in the `HashMap` stored in our struct. If a particular section or key does not exist, it will be automatically created. 723 ///An existing value in the map will be overwritten. You can also set `None` safely. 724 ///## Example 725 ///```rust 726 ///use configparser::ini::Ini; 727 /// 728 ///let mut config = Ini::new(); 729 ///config.read(String::from( 730 /// "[section] 731 /// key=notvalue")); 732 ///config.setstr("section", "key", Some("value")); 733 ///config.setstr("section", "key", None); // also valid! 734 ///assert_eq!(config.get("section", "key"), None); // correct! 735 ///``` 736 ///Returns `None` if there is no existing value, else returns `Some(Option<String>)`, with the existing value being the wrapped `Option<String>`. 737 ///If you want to insert using a `String`, use `set()` instead. setstr( &mut self, section: &str, key: &str, value: Option<&str>, ) -> Option<Option<String>>738 pub fn setstr( 739 &mut self, 740 section: &str, 741 key: &str, 742 value: Option<&str>, 743 ) -> Option<Option<String>> { 744 let (section, key) = self.autocase(section, key); 745 self.set(§ion, &key, value.map(String::from)) 746 } 747 748 ///Clears the map, removing all sections and properties from the hashmap. It keeps the allocated memory for reuse. 749 ///## Example 750 ///```rust 751 ///use configparser::ini::Ini; 752 /// 753 ///let mut config = Ini::new(); 754 ///config.read(String::from( 755 /// "[section] 756 /// key=somevalue")); 757 ///config.clear(); 758 ///assert!(config.get_map_ref().is_empty()); // our map is empty! 759 ///``` 760 ///Returns nothing. clear(&mut self)761 pub fn clear(&mut self) { 762 self.map.clear(); 763 } 764 765 ///Removes a section from the hashmap, returning the properties stored in the section if the section was previously in the map. 766 ///## Example 767 ///```rust 768 ///use configparser::ini::Ini; 769 /// 770 ///let mut config = Ini::new(); 771 ///config.read(String::from( 772 /// "[section] 773 /// updog=whatsupdog")); 774 ///config.remove_section("section"); // this will return a cloned hashmap of the stored property 775 ///assert!(config.get_map_ref().is_empty()); // with the last section removed, our map is now empty! 776 ///``` 777 ///Returns `Some(section_map)` if the section exists or else, `None`. remove_section(&mut self, section: &str) -> Option<HashMap<String, Option<String>>>778 pub fn remove_section(&mut self, section: &str) -> Option<HashMap<String, Option<String>>> { 779 let section = if self.case_sensitive { 780 section.to_owned() 781 } else { 782 section.to_lowercase() 783 }; 784 self.map.remove(§ion) 785 } 786 787 ///Removes a key from a section in the hashmap, returning the value attached to the key if it was previously in the map. 788 ///## Example 789 ///```rust 790 ///use configparser::ini::Ini; 791 /// 792 ///let mut config = Ini::new(); 793 ///config.read(String::from( 794 /// "[section] 795 /// updog=whatsupdog 796 /// [anothersection] 797 /// updog=differentdog")); 798 ///let val = config.remove_key("anothersection", "updog").unwrap().unwrap(); 799 ///assert_eq!(val, String::from("differentdog")); // with the last section removed, our map is now empty! 800 ///``` 801 ///Returns `Some(Option<String>)` if the value exists or else, `None`. remove_key(&mut self, section: &str, key: &str) -> Option<Option<String>>802 pub fn remove_key(&mut self, section: &str, key: &str) -> Option<Option<String>> { 803 let (section, key) = self.autocase(section, key); 804 self.map.get_mut(§ion)?.remove(&key) 805 } 806 } 807