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 #[cfg(feature = "indexmap")] 4 use indexmap::IndexMap as Map; 5 #[cfg(not(feature = "indexmap"))] 6 use std::collections::HashMap as Map; 7 #[cfg(feature = "tokio")] 8 use tokio::fs as async_fs; 9 10 use std::collections::HashMap; 11 use std::convert::AsRef; 12 use std::fmt::Write; 13 use std::fs; 14 use std::path::Path; 15 16 ///The `Ini` struct simply contains a nested hashmap of the loaded configuration, the default section header and comment symbols. 17 ///## Example 18 ///```rust 19 ///use configparser::ini::Ini; 20 /// 21 ///let mut config = Ini::new(); 22 ///``` 23 #[derive(Debug, Clone, Eq, PartialEq, Default)] 24 #[non_exhaustive] 25 pub struct Ini { 26 map: Map<String, Map<String, Option<String>>>, 27 default_section: std::string::String, 28 comment_symbols: Vec<char>, 29 inline_comment_symbols: Option<Vec<char>>, 30 delimiters: Vec<char>, 31 boolean_values: HashMap<bool, Vec<String>>, 32 case_sensitive: bool, 33 multiline: bool, 34 } 35 36 ///The `IniDefault` struct serves as a template to create other `Ini` objects from. It can be used to store and load 37 ///default properties from different `Ini` objects. 38 ///## Example 39 ///```rust 40 ///use configparser::ini::Ini; 41 /// 42 ///let mut config = Ini::new(); 43 ///let default = config.defaults(); 44 ///let mut config2 = Ini::new_from_defaults(default); // default gets consumed 45 ///``` 46 #[derive(Debug, Clone, Eq, PartialEq)] 47 #[non_exhaustive] 48 pub struct IniDefault { 49 ///Denotes the default section header name. 50 ///## Example 51 ///```rust 52 ///use configparser::ini::Ini; 53 /// 54 ///let mut config = Ini::new(); 55 ///let default = config.defaults(); 56 ///assert_eq!(default.default_section, "default"); 57 ///``` 58 pub default_section: std::string::String, 59 ///Denotes the set comment symbols for the object. 60 ///## Example 61 ///```rust 62 ///use configparser::ini::Ini; 63 /// 64 ///let mut config = Ini::new(); 65 ///let default = config.defaults(); 66 ///assert_eq!(default.comment_symbols, vec![';', '#']); 67 ///``` 68 pub comment_symbols: Vec<char>, 69 ///Denotes the set of inline comment symbols for the object. The default of 70 ///`None` means to fall back to the normal comment symbols. 71 ///## Example 72 ///```rust 73 ///use configparser::ini::Ini; 74 /// 75 ///let mut config = Ini::new(); 76 ///let default = config.defaults(); 77 ///assert_eq!(default.inline_comment_symbols, None); 78 ///``` 79 pub inline_comment_symbols: Option<Vec<char>>, 80 ///Denotes the set delimiters for the key-value pairs. 81 ///## Example 82 ///```rust 83 ///use configparser::ini::Ini; 84 /// 85 ///let mut config = Ini::new(); 86 ///let default = config.defaults(); 87 ///assert_eq!(default.delimiters, vec!['=', ':']); 88 ///``` 89 pub delimiters: Vec<char>, 90 pub boolean_values: HashMap<bool, Vec<String>>, 91 ///Denotes if the `Ini` object is case-sensitive. 92 ///## Example 93 ///```rust 94 ///use configparser::ini::Ini; 95 /// 96 ///let mut config = Ini::new(); 97 ///let default = config.defaults(); 98 ///assert_eq!(default.case_sensitive, false); 99 ///``` 100 pub case_sensitive: bool, 101 ///Denotes if the `Ini` object parses multiline strings. 102 ///## Example 103 ///```rust 104 ///use configparser::ini::Ini; 105 /// 106 ///let mut config = Ini::new(); 107 ///let default = config.defaults(); 108 ///assert_eq!(default.multiline, false); 109 ///``` 110 pub multiline: bool, 111 } 112 113 impl Default for IniDefault { default() -> Self114 fn default() -> Self { 115 Self { 116 default_section: "default".to_owned(), 117 comment_symbols: vec![';', '#'], 118 inline_comment_symbols: None, 119 delimiters: vec!['=', ':'], 120 multiline: false, 121 boolean_values: [ 122 ( 123 true, 124 ["true", "yes", "t", "y", "on", "1"] 125 .iter() 126 .map(|&s| s.to_owned()) 127 .collect(), 128 ), 129 ( 130 false, 131 ["false", "no", "f", "n", "off", "0"] 132 .iter() 133 .map(|&s| s.to_owned()) 134 .collect(), 135 ), 136 ] 137 .iter() 138 .cloned() 139 .collect(), 140 case_sensitive: false, 141 } 142 } 143 } 144 145 /// Use this struct to define formatting options for the `pretty_write` functions. 146 #[derive(Debug, Clone, Eq, PartialEq)] 147 #[non_exhaustive] 148 pub struct WriteOptions { 149 ///If true then the keys and values will be separated by " = ". In the special case where the value is empty, the 150 ///line ends with " =". 151 ///If false then keys and values will be separated by "=". 152 ///Default is `false`. 153 ///## Example 154 ///```rust 155 ///use configparser::ini::WriteOptions; 156 /// 157 ///let mut write_options = WriteOptions::default(); 158 ///assert_eq!(write_options.space_around_delimiters, false); 159 ///``` 160 pub space_around_delimiters: bool, 161 162 ///Defines the number of spaces for indentation of for multiline values. 163 ///Default is 4 spaces. 164 ///## Example 165 ///```rust 166 ///use configparser::ini::WriteOptions; 167 /// 168 ///let mut write_options = WriteOptions::default(); 169 ///assert_eq!(write_options.multiline_line_indentation, 4); 170 ///``` 171 pub multiline_line_indentation: usize, 172 173 ///Defines the number of blank lines between sections. 174 ///Default is 0. 175 ///## Example 176 ///```rust 177 ///use configparser::ini::WriteOptions; 178 /// 179 ///let mut write_options = WriteOptions::default(); 180 ///assert_eq!(write_options.blank_lines_between_sections, 0); 181 ///``` 182 pub blank_lines_between_sections: usize, 183 } 184 185 impl Default for WriteOptions { default() -> Self186 fn default() -> Self { 187 Self { 188 space_around_delimiters: false, 189 multiline_line_indentation: 4, 190 blank_lines_between_sections: 0, 191 } 192 } 193 } 194 195 impl WriteOptions { 196 ///Creates a new `WriteOptions` object with the default values. 197 ///## Example 198 ///```rust 199 ///use configparser::ini::WriteOptions; 200 /// 201 ///let write_options = WriteOptions::new(); 202 ///assert_eq!(write_options.space_around_delimiters, false); 203 ///assert_eq!(write_options.multiline_line_indentation, 4); 204 ///assert_eq!(write_options.blank_lines_between_sections, 0); 205 ///``` 206 ///Returns the struct and stores it in the calling variable. new() -> WriteOptions207 pub fn new() -> WriteOptions { 208 WriteOptions::default() 209 } 210 211 ///Creates a new `WriteOptions` object with the given parameters. 212 ///## Example 213 ///```rust 214 ///use configparser::ini::WriteOptions; 215 /// 216 ///let write_options = WriteOptions::new_with_params(true, 2, 1); 217 ///assert_eq!(write_options.space_around_delimiters, true); 218 ///assert_eq!(write_options.multiline_line_indentation, 2); 219 ///assert_eq!(write_options.blank_lines_between_sections, 1); 220 ///``` 221 ///Returns the struct and stores it in the calling variable. new_with_params( space_around_delimiters: bool, multiline_line_indentation: usize, blank_lines_between_sections: usize, ) -> WriteOptions222 pub fn new_with_params( 223 space_around_delimiters: bool, 224 multiline_line_indentation: usize, 225 blank_lines_between_sections: usize, 226 ) -> WriteOptions { 227 Self { 228 space_around_delimiters, 229 multiline_line_indentation, 230 blank_lines_between_sections, 231 } 232 } 233 } 234 235 #[cfg(windows)] 236 const LINE_ENDING: &str = "\r\n"; 237 #[cfg(not(windows))] 238 const LINE_ENDING: &str = "\n"; 239 240 impl Ini { 241 ///Creates a new `Map` of `Map<String, Map<String, Option<String>>>` type for the struct. 242 ///All values in the Map are stored in `String` type. 243 /// 244 ///By default, [`std::collections::HashMap`] is used for the Map object. 245 ///The `indexmap` feature can be used to use an [`indexmap::map::IndexMap`] instead, which 246 ///allows keeping the insertion order for sections and keys. 247 /// 248 ///## Example 249 ///```rust 250 ///use configparser::ini::Ini; 251 /// 252 ///let mut config = Ini::new(); 253 ///``` 254 ///Returns the struct and stores it in the calling variable. new() -> Ini255 pub fn new() -> Ini { 256 Ini::new_from_defaults(IniDefault::default()) 257 } 258 259 ///Creates a new **case-sensitive** `Map` of `Map<String, Map<String, Option<String>>>` type for the struct. 260 ///All values in the Map are stored in `String` type. 261 ///## Example 262 ///```rust 263 ///use configparser::ini::Ini; 264 /// 265 ///let mut config = Ini::new_cs(); 266 ///``` 267 ///Returns the struct and stores it in the calling variable. new_cs() -> Ini268 pub fn new_cs() -> Ini { 269 Ini::new_from_defaults(IniDefault { 270 case_sensitive: true, 271 ..Default::default() 272 }) 273 } 274 275 ///Creates a new `Ini` with the given defaults from an existing `IniDefault` object. 276 ///## Example 277 ///```rust 278 ///use configparser::ini::Ini; 279 ///use configparser::ini::IniDefault; 280 /// 281 ///let mut default = IniDefault::default(); 282 ///default.comment_symbols = vec![';']; 283 ///default.delimiters = vec!['=']; 284 ///let mut config = Ini::new_from_defaults(default.clone()); 285 ///// Now, load as usual with new defaults: 286 ///let map = config.load("tests/test.ini").unwrap(); 287 ///assert_eq!(config.defaults(), default); 288 /// 289 ///``` new_from_defaults(defaults: IniDefault) -> Ini290 pub fn new_from_defaults(defaults: IniDefault) -> Ini { 291 Ini { 292 map: Map::new(), 293 default_section: defaults.default_section, 294 comment_symbols: defaults.comment_symbols, 295 inline_comment_symbols: defaults.inline_comment_symbols, 296 delimiters: defaults.delimiters, 297 boolean_values: defaults.boolean_values, 298 case_sensitive: defaults.case_sensitive, 299 multiline: defaults.multiline, 300 } 301 } 302 303 ///Fetches the defaults from the current `Ini` object and stores it as a `IniDefault` struct for usage elsewhere. 304 ///## Example 305 ///```rust 306 ///use configparser::ini::Ini; 307 /// 308 ///let mut config = Ini::new(); 309 ///let default = config.defaults(); 310 ///``` 311 ///Returns an `IniDefault` object. Keep in mind that it will get borrowed since it has non-`Copy` types. defaults(&self) -> IniDefault312 pub fn defaults(&self) -> IniDefault { 313 IniDefault { 314 default_section: self.default_section.to_owned(), 315 comment_symbols: self.comment_symbols.to_owned(), 316 inline_comment_symbols: self.inline_comment_symbols.to_owned(), 317 delimiters: self.delimiters.to_owned(), 318 boolean_values: self.boolean_values.to_owned(), 319 case_sensitive: self.case_sensitive, 320 multiline: self.multiline, 321 } 322 } 323 324 ///Takes an `IniDefault` object and stores its properties in the calling `Ini` object. This happens in-place and 325 ///does not work retroactively, only future operations are affected. 326 ///## Example 327 ///```rust 328 ///use configparser::ini::Ini; 329 ///use configparser::ini::IniDefault; 330 /// 331 ///let mut config = Ini::new(); 332 ///let mut default = IniDefault::default(); 333 ///default.case_sensitive = true; 334 ///// This is equivalent to ini_cs() defaults 335 ///config.load_defaults(default.clone()); 336 ///assert_eq!(config.defaults(), default); 337 ///``` 338 ///Returns nothing. load_defaults(&mut self, defaults: IniDefault)339 pub fn load_defaults(&mut self, defaults: IniDefault) { 340 self.default_section = defaults.default_section; 341 self.comment_symbols = defaults.comment_symbols; 342 self.inline_comment_symbols = defaults.inline_comment_symbols; 343 self.delimiters = defaults.delimiters; 344 self.boolean_values = defaults.boolean_values; 345 self.case_sensitive = defaults.case_sensitive; 346 } 347 348 ///Sets the default section header to the defined string (the default is `default`). 349 ///It must be set before `load()` or `read()` is called in order to take effect. 350 ///## Example 351 ///```rust 352 ///use configparser::ini::Ini; 353 /// 354 ///let mut config = Ini::new(); 355 /// 356 ///config.set_default_section("topsecret"); 357 ///let map = config.load("tests/test.ini").unwrap(); 358 ///``` 359 ///Returns nothing. set_default_section(&mut self, section: &str)360 pub fn set_default_section(&mut self, section: &str) { 361 self.default_section = section.to_owned(); 362 } 363 364 ///Sets the default comment symbols to the defined character slice (the defaults are `;` and `#`). 365 ///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. 366 ///## Example 367 ///```rust 368 ///use configparser::ini::Ini; 369 /// 370 ///let mut config = Ini::new(); 371 ///config.set_comment_symbols(&['!', '#']); 372 ///let map = config.load("tests/test.ini").unwrap(); 373 ///``` 374 ///Returns nothing. set_comment_symbols(&mut self, symlist: &[char])375 pub fn set_comment_symbols(&mut self, symlist: &[char]) { 376 self.comment_symbols = symlist.to_vec(); 377 } 378 379 ///Sets the default inline comment symbols to the defined character slice (the default is `None` which falls back to the normal comment symbols). 380 ///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. 381 ///## Example 382 ///```rust 383 ///use configparser::ini::Ini; 384 /// 385 ///let mut config = Ini::new(); 386 ///config.set_inline_comment_symbols(Some(&['!', '#'])); 387 ///let map = config.load("tests/test.ini").unwrap(); 388 ///``` 389 ///Returns nothing. set_inline_comment_symbols(&mut self, symlist: Option<&[char]>)390 pub fn set_inline_comment_symbols(&mut self, symlist: Option<&[char]>) { 391 self.inline_comment_symbols = symlist.map(|val| val.to_vec()); 392 } 393 394 ///Sets multiline string support. 395 ///It must be set before `load()` or `read()` is called in order to take effect. 396 ///## Example 397 ///```rust 398 ///use configparser::ini::Ini; 399 /// 400 ///let mut config = Ini::new(); 401 ///config.set_multiline(true); 402 ///let map = config.load("tests/test.ini").unwrap(); 403 ///``` 404 ///Returns nothing. set_multiline(&mut self, multiline: bool)405 pub fn set_multiline(&mut self, multiline: bool) { 406 self.multiline = multiline; 407 } 408 409 ///Gets all the sections of the currently-stored `Map` in a vector. 410 ///## Example 411 ///```rust 412 ///use configparser::ini::Ini; 413 /// 414 ///let mut config = Ini::new(); 415 ///config.load("tests/test.ini"); 416 ///let sections = config.sections(); 417 ///``` 418 ///Returns `Vec<String>`. sections(&self) -> Vec<String>419 pub fn sections(&self) -> Vec<String> { 420 self.map.keys().cloned().collect() 421 } 422 423 ///Loads a file from a defined path, parses it and puts the hashmap into our struct. 424 ///At one time, it only stores one configuration, so each call to `load()` or `read()` will clear the existing `Map`, if present. 425 ///## Example 426 ///```rust 427 ///use configparser::ini::Ini; 428 /// 429 ///let mut config = Ini::new(); 430 ///let map = config.load("tests/test.ini").unwrap(); // we can get a clone like this, or just store it 431 /////Then, we can use standard hashmap functions like: 432 ///let values = map.get("values").unwrap(); 433 ///``` 434 ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`. 435 ///Use `get_mut_map()` if you want a mutable reference. load<T: AsRef<Path>>( &mut self, path: T, ) -> Result<Map<String, Map<String, Option<String>>>, String>436 pub fn load<T: AsRef<Path>>( 437 &mut self, 438 path: T, 439 ) -> Result<Map<String, Map<String, Option<String>>>, String> { 440 self.map = match self.parse(match fs::read_to_string(&path) { 441 Err(why) => { 442 return Err(format!( 443 "couldn't read {}: {}", 444 &path.as_ref().display(), 445 why 446 )) 447 } 448 Ok(s) => s, 449 }) { 450 Err(why) => { 451 return Err(format!( 452 "couldn't read {}: {}", 453 &path.as_ref().display(), 454 why 455 )) 456 } 457 Ok(map) => map, 458 }; 459 Ok(self.map.clone()) 460 } 461 462 ///Loads a file from a defined path, parses it and applies it to the existing hashmap in our struct. 463 ///While `load()` will clear the existing `Map`, `load_and_append()` applies the new values on top of 464 ///the existing hashmap, preserving previous values. 465 ///## Example 466 ///```rust 467 ///use configparser::ini::Ini; 468 /// 469 ///let mut config = Ini::new(); 470 ///config.load("tests/test.ini").unwrap(); 471 ///config.load_and_append("tests/sys_cfg.ini").ok(); // we don't have to worry if this doesn't succeed 472 ///config.load_and_append("tests/user_cfg.ini").ok(); // we don't have to worry if this doesn't succeed 473 ///let map = config.get_map().unwrap(); 474 /////Then, we can use standard hashmap functions like: 475 ///let values = map.get("values").unwrap(); 476 ///``` 477 ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`. 478 ///Use `get_mut_map()` if you want a mutable reference. load_and_append<T: AsRef<Path>>( &mut self, path: T, ) -> Result<Map<String, Map<String, Option<String>>>, String>479 pub fn load_and_append<T: AsRef<Path>>( 480 &mut self, 481 path: T, 482 ) -> Result<Map<String, Map<String, Option<String>>>, String> { 483 let loaded = match self.parse(match fs::read_to_string(&path) { 484 Err(why) => { 485 return Err(format!( 486 "couldn't read {}: {}", 487 &path.as_ref().display(), 488 why 489 )) 490 } 491 Ok(s) => s, 492 }) { 493 Err(why) => { 494 return Err(format!( 495 "couldn't read {}: {}", 496 &path.as_ref().display(), 497 why 498 )) 499 } 500 Ok(map) => map, 501 }; 502 503 for (section, section_map) in loaded.iter() { 504 self.map 505 .entry(section.clone()) 506 .or_default() 507 .extend(section_map.clone()); 508 } 509 510 Ok(self.map.clone()) 511 } 512 513 ///Reads an input string, parses it and puts the hashmap into our struct. 514 ///At one time, it only stores one configuration, so each call to `load()` or `read()` will clear the existing `Map`, if present. 515 ///## Example 516 ///```rust 517 ///use configparser::ini::Ini; 518 /// 519 ///let mut config = Ini::new(); 520 ///let map = match config.read(String::from( 521 /// "[2000s] 522 /// 2020 = bad")) { 523 /// Err(why) => panic!("{}", why), 524 /// Ok(inner) => inner 525 ///}; 526 ///let this_year = map["2000s"]["2020"].clone().unwrap(); 527 ///assert_eq!(this_year, "bad"); // value accessible! 528 ///``` 529 ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`. 530 ///Use `get_mut_map()` if you want a mutable reference. read( &mut self, input: String, ) -> Result<Map<String, Map<String, Option<String>>>, String>531 pub fn read( 532 &mut self, 533 input: String, 534 ) -> Result<Map<String, Map<String, Option<String>>>, String> { 535 self.map = match self.parse(input) { 536 Err(why) => return Err(why), 537 Ok(map) => map, 538 }; 539 Ok(self.map.clone()) 540 } 541 542 ///Reads an input string, parses it and applies it to the existing hashmap in our struct. 543 ///While `read()` and `load()` will clear the existing `Map`, `read_and_append()` applies the new 544 ///values on top of the existing hashmap, preserving previous values. 545 ///## Example 546 ///```rust 547 ///use configparser::ini::Ini; 548 /// 549 ///let mut config = Ini::new(); 550 ///if let Err(why) = config.read(String::from( 551 /// "[2000s] 552 /// 2020 = bad 553 /// 2023 = better")) { 554 /// panic!("{}", why); 555 ///}; 556 ///if let Err(why) = config.read_and_append(String::from( 557 /// "[2000s] 558 /// 2020 = terrible")) { 559 /// panic!("{}", why); 560 ///}; 561 ///let map = config.get_map().unwrap(); 562 ///let few_years_ago = map["2000s"]["2020"].clone().unwrap(); 563 ///let this_year = map["2000s"]["2023"].clone().unwrap(); 564 ///assert_eq!(few_years_ago, "terrible"); // value updated! 565 ///assert_eq!(this_year, "better"); // keeps old values! 566 ///``` 567 ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`. 568 ///Use `get_mut_map()` if you want a mutable reference. read_and_append( &mut self, input: String, ) -> Result<Map<String, Map<String, Option<String>>>, String>569 pub fn read_and_append( 570 &mut self, 571 input: String, 572 ) -> Result<Map<String, Map<String, Option<String>>>, String> { 573 let loaded = match self.parse(input) { 574 Err(why) => return Err(why), 575 Ok(map) => map, 576 }; 577 578 for (section, section_map) in loaded.iter() { 579 self.map 580 .entry(section.clone()) 581 .or_default() 582 .extend(section_map.clone()); 583 } 584 585 Ok(self.map.clone()) 586 } 587 588 ///Writes the current configuation to the specified path using default formatting. 589 ///If a file is not present then it is automatically created for you. If a file already exists then it is overwritten. 590 ///## Example 591 ///```rust 592 ///use configparser::ini::Ini; 593 /// 594 ///fn main() -> std::io::Result<()> { 595 /// let mut config = Ini::new(); 596 /// config.read(String::from( 597 /// "[2000s] 598 /// 2020 = bad")); 599 /// config.write("output.ini") 600 ///} 601 ///``` 602 ///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<()>603 pub fn write<T: AsRef<Path>>(&self, path: T) -> std::io::Result<()> { 604 fs::write(path.as_ref(), self.unparse(&WriteOptions::default())) 605 } 606 607 ///Writes the current configuation to the specified path using the given formatting options. 608 ///If a file is not present then it is automatically created for you. If a file already exists then it is overwritten. 609 ///## Example 610 ///```rust 611 ///use configparser::ini::{Ini, WriteOptions}; 612 /// 613 ///fn main() -> std::io::Result<()> { 614 /// let mut write_options = WriteOptions::default(); 615 /// write_options.space_around_delimiters = true; 616 /// write_options.multiline_line_indentation = 2; 617 /// write_options.blank_lines_between_sections = 1; 618 /// 619 /// let mut config = Ini::new(); 620 /// config.read(String::from( 621 /// "[2000s] 622 /// 2020 = bad")); 623 /// config.pretty_write("output.ini", &write_options) 624 ///} 625 ///``` 626 ///Returns a `std::io::Result<()>` type dependent on whether the write was successful or not. pretty_write<T: AsRef<Path>>( &self, path: T, write_options: &WriteOptions, ) -> std::io::Result<()>627 pub fn pretty_write<T: AsRef<Path>>( 628 &self, 629 path: T, 630 write_options: &WriteOptions, 631 ) -> std::io::Result<()> { 632 fs::write(path.as_ref(), self.unparse(write_options)) 633 } 634 635 ///Returns a string with the current configuration formatted with valid ini-syntax using default formatting. 636 ///This is always safe since the configuration is validated during parsing. 637 ///## Example 638 ///```rust 639 ///use configparser::ini::Ini; 640 /// 641 ///let mut config = Ini::new(); 642 ///config.read(String::from( 643 /// "[2000s] 644 /// 2020 = bad")); 645 ///let outstring = config.writes(); 646 ///``` 647 ///Returns a `String` type contatining the ini-syntax file. writes(&self) -> String648 pub fn writes(&self) -> String { 649 self.unparse(&WriteOptions::default()) 650 } 651 652 ///Returns a string with the current configuration formatted with valid ini-syntax using the given formatting options. 653 ///This is always safe since the configuration is validated during parsing. 654 ///## Example 655 ///```rust 656 ///use configparser::ini::{Ini, WriteOptions}; 657 /// 658 ///let mut write_options = WriteOptions::default(); 659 ///write_options.space_around_delimiters = true; 660 ///write_options.multiline_line_indentation = 2; 661 ///write_options.blank_lines_between_sections = 1; 662 /// 663 ///let mut config = Ini::new(); 664 ///config.read(String::from( 665 /// "[2000s] 666 /// 2020 = bad")); 667 ///let outstring = config.pretty_writes(&write_options); 668 ///``` 669 ///Returns a `String` type contatining the ini-syntax file. pretty_writes(&self, write_options: &WriteOptions) -> String670 pub fn pretty_writes(&self, write_options: &WriteOptions) -> String { 671 self.unparse(write_options) 672 } 673 674 ///Private function that converts the currently stored configuration into a valid ini-syntax string. unparse(&self, write_options: &WriteOptions) -> String675 fn unparse(&self, write_options: &WriteOptions) -> String { 676 // push key/value pairs in outmap to out string. 677 fn unparse_key_values( 678 out: &mut String, 679 outmap: &Map<String, Option<String>>, 680 multiline: bool, 681 space_around_delimiters: bool, 682 indent: usize, 683 ) { 684 let delimiter = if space_around_delimiters { " = " } else { "=" }; 685 for (key, val) in outmap.iter() { 686 out.push_str(key); 687 688 if let Some(value) = val { 689 if value.is_empty() { 690 out.push_str(delimiter.trim_end()); 691 } else { 692 out.push_str(delimiter); 693 } 694 695 if multiline { 696 let mut lines = value.lines(); 697 698 out.push_str(lines.next().unwrap_or_default()); 699 700 for line in lines { 701 out.push_str(LINE_ENDING); 702 if !line.is_empty() { 703 out.push_str(" ".repeat(indent).as_ref()); 704 out.push_str(line); 705 } 706 } 707 } else { 708 out.push_str(value); 709 } 710 } 711 712 out.push_str(LINE_ENDING); 713 } 714 } 715 716 let line_endings = LINE_ENDING.repeat(write_options.blank_lines_between_sections); 717 let mut out = String::new(); 718 719 if let Some(defaultmap) = self.map.get(&self.default_section) { 720 unparse_key_values( 721 &mut out, 722 defaultmap, 723 self.multiline, 724 write_options.space_around_delimiters, 725 write_options.multiline_line_indentation, 726 ); 727 } 728 729 let mut is_first = true; 730 for (section, secmap) in self.map.iter() { 731 if !is_first { 732 out.push_str(line_endings.as_ref()); 733 } 734 if section != &self.default_section { 735 write!(out, "[{}]", section).unwrap(); 736 out.push_str(LINE_ENDING); 737 unparse_key_values( 738 &mut out, 739 secmap, 740 self.multiline, 741 write_options.space_around_delimiters, 742 write_options.multiline_line_indentation, 743 ); 744 } 745 is_first = false; 746 } 747 out 748 } 749 750 ///Private function that parses ini-style syntax into a Map. parse(&self, input: String) -> Result<Map<String, Map<String, Option<String>>>, String>751 fn parse(&self, input: String) -> Result<Map<String, Map<String, Option<String>>>, String> { 752 let inline_comment_symbols: &[char] = self 753 .inline_comment_symbols 754 .as_deref() 755 .unwrap_or_else(|| self.comment_symbols.as_ref()); 756 let mut map: Map<String, Map<String, Option<String>>> = Map::new(); 757 let mut section = self.default_section.clone(); 758 let mut current_key: Option<String> = None; 759 760 let caser = |val: &str| { 761 if self.case_sensitive { 762 val.to_owned() 763 } else { 764 val.to_lowercase() 765 } 766 }; 767 768 // Track blank lines to preserve them in multiline values. 769 let mut blank_lines = 0usize; 770 771 for (num, raw_line) in input.lines().enumerate() { 772 let line = raw_line.trim(); 773 774 // If the line is _just_ a comment, skip it entirely. 775 let line = match line.find(|c: char| self.comment_symbols.contains(&c)) { 776 Some(0) => continue, 777 Some(_) | None => line, 778 }; 779 780 let line = line.trim(); 781 782 // Skip empty lines, but keep track of them for multiline values. 783 if line.is_empty() { 784 blank_lines += 1; 785 continue; 786 } 787 788 let line = match line.find(|c: char| inline_comment_symbols.contains(&c)) { 789 Some(idx) => &line[..idx], 790 None => line, 791 }; 792 793 let trimmed = line.trim(); 794 795 match (trimmed.find('['), trimmed.rfind(']')) { 796 (Some(0), Some(end)) => { 797 section = caser(trimmed[1..end].trim()); 798 799 map.entry(section.clone()).or_default(); 800 801 continue; 802 } 803 (Some(0), None) => { 804 return Err(format!( 805 "line {}: Found opening bracket for section name but no closing bracket", 806 num 807 )); 808 } 809 _ => {} 810 } 811 812 if raw_line.starts_with(char::is_whitespace) && self.multiline { 813 let key = match current_key.as_ref() { 814 Some(x) => x, 815 None => { 816 return Err(format!( 817 "line {}: Started with indentation but there is no current entry", 818 num, 819 )) 820 } 821 }; 822 823 let valmap = map.entry(section.clone()).or_default(); 824 825 let val = valmap 826 .entry(key.clone()) 827 .or_insert_with(|| Some(String::new())); 828 829 match val { 830 Some(s) => { 831 for _ in 0..blank_lines { 832 s.push_str(LINE_ENDING); 833 } 834 s.push_str(LINE_ENDING); 835 s.push_str(trimmed); 836 } 837 None => { 838 let mut s = String::with_capacity( 839 (blank_lines + 1) * LINE_ENDING.len() + trimmed.len(), 840 ); 841 for _ in 0..blank_lines { 842 s.push_str(LINE_ENDING); 843 } 844 s.push_str(LINE_ENDING); 845 s.push_str(trimmed); 846 *val = Some(s); 847 } 848 } 849 } else { 850 let valmap = map.entry(section.clone()).or_default(); 851 852 match trimmed.find(&self.delimiters[..]) { 853 Some(delimiter) => { 854 let key = caser(trimmed[..delimiter].trim()); 855 856 if key.is_empty() { 857 return Err(format!("line {}:{}: Key cannot be empty", num, delimiter)); 858 } else { 859 current_key = Some(key.clone()); 860 861 let value = trimmed[delimiter + 1..].trim().to_owned(); 862 863 valmap.insert(key, Some(value)); 864 } 865 } 866 None => { 867 let key = caser(trimmed); 868 current_key = Some(key.clone()); 869 870 valmap.insert(key, None); 871 } 872 } 873 } 874 875 blank_lines = 0; 876 } 877 878 Ok(map) 879 } 880 881 ///Private function that cases things automatically depending on the set variable. autocase(&self, section: &str, key: &str) -> (String, String)882 fn autocase(&self, section: &str, key: &str) -> (String, String) { 883 if self.case_sensitive { 884 (section.to_owned(), key.to_owned()) 885 } else { 886 (section.to_lowercase(), key.to_lowercase()) 887 } 888 } 889 890 ///Returns a clone of the stored value from the key stored in the defined section. 891 ///Unlike accessing the map directly, `get()` can process your input to make case-insensitive access *if* the 892 ///default constructor is used. 893 ///All `get` functions will do this automatically under the hood. 894 ///## Example 895 ///```rust 896 ///use configparser::ini::Ini; 897 /// 898 ///let mut config = Ini::new(); 899 ///config.load("tests/test.ini"); 900 ///let value = config.get("default", "defaultvalues").unwrap(); 901 ///assert_eq!(value, String::from("defaultvalues")); 902 ///``` 903 ///Returns `Some(value)` of type `String` if value is found or else returns `None`. get(&self, section: &str, key: &str) -> Option<String>904 pub fn get(&self, section: &str, key: &str) -> Option<String> { 905 let (section, key) = self.autocase(section, key); 906 self.map.get(§ion)?.get(&key)?.clone() 907 } 908 909 ///Parses the stored value from the key stored in the defined section to a `bool`. 910 ///For ease of use, the function converts the type case-insensitively (`true` == `True`). 911 ///## Example 912 ///```rust 913 ///use configparser::ini::Ini; 914 /// 915 ///let mut config = Ini::new(); 916 ///config.load("tests/test.ini"); 917 ///let value = config.getbool("values", "bool").unwrap().unwrap(); 918 ///assert!(value); // value accessible! 919 ///``` 920 ///Returns `Ok(Some(value))` of type `bool` if value is found or else returns `Ok(None)`. 921 ///If the parsing fails, it returns an `Err(string)`. getbool(&self, section: &str, key: &str) -> Result<Option<bool>, String>922 pub fn getbool(&self, section: &str, key: &str) -> Result<Option<bool>, String> { 923 let (section, key) = self.autocase(section, key); 924 match self.map.get(§ion) { 925 Some(secmap) => match secmap.get(&key) { 926 Some(val) => match val { 927 Some(inner) => match inner.to_lowercase().parse::<bool>() { 928 Err(why) => Err(why.to_string()), 929 Ok(boolean) => Ok(Some(boolean)), 930 }, 931 None => Ok(None), 932 }, 933 None => Ok(None), 934 }, 935 None => Ok(None), 936 } 937 } 938 939 ///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. 940 ///It attempts to case-insenstively find `true`, `yes`, `t`, `y`, `1` and `on` to parse it as `True`. 941 ///Similarly it attempts to case-insensitvely find `false`, `no`, `f`, `n`, `0` and `off` to parse it as `False`. 942 ///## Example 943 ///```rust 944 ///use configparser::ini::Ini; 945 /// 946 ///let mut config = Ini::new(); 947 ///config.load("tests/test.ini"); 948 ///let value = config.getboolcoerce("values", "boolcoerce").unwrap().unwrap(); 949 ///assert!(!value); // value accessible! 950 ///``` 951 ///Returns `Ok(Some(value))` of type `bool` if value is found or else returns `Ok(None)`. 952 ///If the parsing fails, it returns an `Err(string)`. getboolcoerce(&self, section: &str, key: &str) -> Result<Option<bool>, String>953 pub fn getboolcoerce(&self, section: &str, key: &str) -> Result<Option<bool>, String> { 954 let (section, key) = self.autocase(section, key); 955 match self.map.get(§ion) { 956 Some(secmap) => match secmap.get(&key) { 957 Some(val) => match val { 958 Some(inner) => { 959 let boolval = &inner.to_lowercase()[..]; 960 if self 961 .boolean_values 962 .get(&true) 963 .unwrap() 964 .iter() 965 .any(|elem| elem == boolval) 966 { 967 Ok(Some(true)) 968 } else if self 969 .boolean_values 970 .get(&false) 971 .unwrap() 972 .iter() 973 .any(|elem| elem == boolval) 974 { 975 Ok(Some(false)) 976 } else { 977 Err(format!( 978 "Unable to parse value into bool at {}:{}", 979 section, key 980 )) 981 } 982 } 983 None => Ok(None), 984 }, 985 None => Ok(None), 986 }, 987 None => Ok(None), 988 } 989 } 990 991 ///Parses the stored value from the key stored in the defined section to an `i64`. 992 ///## Example 993 ///```rust 994 ///use configparser::ini::Ini; 995 /// 996 ///let mut config = Ini::new(); 997 ///config.load("tests/test.ini"); 998 ///let value = config.getint("values", "int").unwrap().unwrap(); 999 ///assert_eq!(value, -31415); // value accessible! 1000 ///``` 1001 ///Returns `Ok(Some(value))` of type `i64` if value is found or else returns `Ok(None)`. 1002 ///If the parsing fails, it returns an `Err(string)`. getint(&self, section: &str, key: &str) -> Result<Option<i64>, String>1003 pub fn getint(&self, section: &str, key: &str) -> Result<Option<i64>, String> { 1004 let (section, key) = self.autocase(section, key); 1005 match self.map.get(§ion) { 1006 Some(secmap) => match secmap.get(&key) { 1007 Some(val) => match val { 1008 Some(inner) => match inner.parse::<i64>() { 1009 Err(why) => Err(why.to_string()), 1010 Ok(int) => Ok(Some(int)), 1011 }, 1012 None => Ok(None), 1013 }, 1014 None => Ok(None), 1015 }, 1016 None => Ok(None), 1017 } 1018 } 1019 1020 ///Parses the stored value from the key stored in the defined section to a `u64`. 1021 ///## Example 1022 ///```rust 1023 ///use configparser::ini::Ini; 1024 /// 1025 ///let mut config = Ini::new(); 1026 ///config.load("tests/test.ini"); 1027 ///let value = config.getint("values", "Uint").unwrap().unwrap(); 1028 ///assert_eq!(value, 31415); // value accessible! 1029 ///``` 1030 ///Returns `Ok(Some(value))` of type `u64` if value is found or else returns `Ok(None)`. 1031 ///If the parsing fails, it returns an `Err(string)`. getuint(&self, section: &str, key: &str) -> Result<Option<u64>, String>1032 pub fn getuint(&self, section: &str, key: &str) -> Result<Option<u64>, String> { 1033 let (section, key) = self.autocase(section, key); 1034 match self.map.get(§ion) { 1035 Some(secmap) => match secmap.get(&key) { 1036 Some(val) => match val { 1037 Some(inner) => match inner.parse::<u64>() { 1038 Err(why) => Err(why.to_string()), 1039 Ok(uint) => Ok(Some(uint)), 1040 }, 1041 None => Ok(None), 1042 }, 1043 None => Ok(None), 1044 }, 1045 None => Ok(None), 1046 } 1047 } 1048 1049 ///Parses the stored value from the key stored in the defined section to a `f64`. 1050 ///## Example 1051 ///```rust 1052 ///use configparser::ini::Ini; 1053 /// 1054 ///let mut config = Ini::new(); 1055 ///config.load("tests/test.ini"); 1056 ///let value = config.getfloat("values", "float").unwrap().unwrap(); 1057 ///assert_eq!(value, 3.1415); // value accessible! 1058 ///``` 1059 ///Returns `Ok(Some(value))` of type `f64` if value is found or else returns `Ok(None)`. 1060 ///If the parsing fails, it returns an `Err(string)`. getfloat(&self, section: &str, key: &str) -> Result<Option<f64>, String>1061 pub fn getfloat(&self, section: &str, key: &str) -> Result<Option<f64>, String> { 1062 let (section, key) = self.autocase(section, key); 1063 match self.map.get(§ion) { 1064 Some(secmap) => match secmap.get(&key) { 1065 Some(val) => match val { 1066 Some(inner) => match inner.parse::<f64>() { 1067 Err(why) => Err(why.to_string()), 1068 Ok(float) => Ok(Some(float)), 1069 }, 1070 None => Ok(None), 1071 }, 1072 None => Ok(None), 1073 }, 1074 None => Ok(None), 1075 } 1076 } 1077 1078 ///Returns a clone of the `Map` stored in our struct. 1079 ///## Example 1080 ///```rust 1081 ///use configparser::ini::Ini; 1082 /// 1083 ///let mut config = Ini::new(); 1084 ///config.read(String::from( 1085 /// "[section] 1086 /// key=values")); 1087 ///let map = config.get_map().unwrap(); 1088 ///assert_eq!(map, *config.get_map_ref()); // the cloned map is basically a snapshot that you own 1089 ///``` 1090 ///Returns `Some(map)` if map is non-empty or else returns `None`. 1091 ///Similar to `load()` but returns an `Option` type with the currently stored `Map`. get_map(&self) -> Option<Map<String, Map<String, Option<String>>>>1092 pub fn get_map(&self) -> Option<Map<String, Map<String, Option<String>>>> { 1093 if self.map.is_empty() { 1094 None 1095 } else { 1096 Some(self.map.clone()) 1097 } 1098 } 1099 1100 ///Returns an immutable reference to the `Map` stored in our struct. 1101 ///## Example 1102 ///```rust 1103 ///use configparser::ini::Ini; 1104 /// 1105 ///let mut config = Ini::new(); 1106 ///let mapclone = config.read(String::from 1107 /// ("[topsecrets] 1108 /// Valueless key")).unwrap(); 1109 /////Think of the clone as being a snapshot at a point of time while the reference always points to the current configuration. 1110 ///assert_eq!(*config.get_map_ref(), mapclone); // same as expected. 1111 ///``` 1112 ///If you just need to definitely mutate the map, use `get_mut_map()` instead. Alternatively, you can generate a snapshot by getting a clone 1113 ///with `get_map()` and work with that. get_map_ref(&self) -> &Map<String, Map<String, Option<String>>>1114 pub fn get_map_ref(&self) -> &Map<String, Map<String, Option<String>>> { 1115 &self.map 1116 } 1117 1118 ///Returns a mutable reference to the `Map` stored in our struct. 1119 ///## Example 1120 ///```rust 1121 ///use configparser::ini::Ini; 1122 /// 1123 ///let mut config = Ini::new(); 1124 ///config.read(String::from 1125 /// ("[topsecrets] 1126 /// Valueless key")); 1127 /////We can then get the mutable map and insert a value like: 1128 ///config.get_mut_map().get_mut("topsecrets").unwrap().insert(String::from("nuclear launch codes"), None); 1129 ///assert_eq!(config.get("topsecrets", "nuclear launch codes"), None); // inserted successfully! 1130 ///``` 1131 ///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 Map<String, Map<String, Option<String>>>1132 pub fn get_mut_map(&mut self) -> &mut Map<String, Map<String, Option<String>>> { 1133 &mut self.map 1134 } 1135 1136 ///Sets an `Option<String>` in the `Map` stored in our struct. If a particular section or key does not exist, it will be automatically created. 1137 ///An existing value in the map will be overwritten. You can also set `None` safely. 1138 ///## Example 1139 ///```rust 1140 ///use configparser::ini::Ini; 1141 /// 1142 ///let mut config = Ini::new(); 1143 ///config.read(String::from( 1144 /// "[section] 1145 /// key=value")); 1146 ///let key_value = String::from("value"); 1147 ///config.set("section", "key", Some(key_value)); 1148 ///config.set("section", "key", None); // also valid! 1149 ///assert_eq!(config.get("section", "key"), None); // correct! 1150 ///``` 1151 ///Returns `None` if there is no existing value, else returns `Some(Option<String>)`, with the existing value being the wrapped `Option<String>`. 1152 ///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>>1153 pub fn set( 1154 &mut self, 1155 section: &str, 1156 key: &str, 1157 value: Option<String>, 1158 ) -> Option<Option<String>> { 1159 let (section, key) = self.autocase(section, key); 1160 match self.map.get_mut(§ion) { 1161 Some(secmap) => secmap.insert(key, value), 1162 None => { 1163 let mut valmap: Map<String, Option<String>> = Map::new(); 1164 valmap.insert(key, value); 1165 self.map.insert(section, valmap); 1166 None 1167 } 1168 } 1169 } 1170 1171 ///Sets an `Option<&str>` in the `Map` stored in our struct. If a particular section or key does not exist, it will be automatically created. 1172 ///An existing value in the map will be overwritten. You can also set `None` safely. 1173 ///## Example 1174 ///```rust 1175 ///use configparser::ini::Ini; 1176 /// 1177 ///let mut config = Ini::new(); 1178 ///config.read(String::from( 1179 /// "[section] 1180 /// key=notvalue")); 1181 ///config.setstr("section", "key", Some("value")); 1182 ///config.setstr("section", "key", None); // also valid! 1183 ///assert_eq!(config.get("section", "key"), None); // correct! 1184 ///``` 1185 ///Returns `None` if there is no existing value, else returns `Some(Option<String>)`, with the existing value being the wrapped `Option<String>`. 1186 ///If you want to insert using a `String`, use `set()` instead. setstr( &mut self, section: &str, key: &str, value: Option<&str>, ) -> Option<Option<String>>1187 pub fn setstr( 1188 &mut self, 1189 section: &str, 1190 key: &str, 1191 value: Option<&str>, 1192 ) -> Option<Option<String>> { 1193 let (section, key) = self.autocase(section, key); 1194 self.set(§ion, &key, value.map(String::from)) 1195 } 1196 1197 ///Clears the map, removing all sections and properties from the hashmap. It keeps the allocated memory for reuse. 1198 ///## Example 1199 ///```rust 1200 ///use configparser::ini::Ini; 1201 /// 1202 ///let mut config = Ini::new(); 1203 ///config.read(String::from( 1204 /// "[section] 1205 /// key=somevalue")); 1206 ///config.clear(); 1207 ///assert!(config.get_map_ref().is_empty()); // our map is empty! 1208 ///``` 1209 ///Returns nothing. clear(&mut self)1210 pub fn clear(&mut self) { 1211 self.map.clear(); 1212 } 1213 1214 ///Removes a section from the hashmap, returning the properties stored in the section if the section was previously in the map. 1215 ///## Example 1216 ///```rust 1217 ///use configparser::ini::Ini; 1218 /// 1219 ///let mut config = Ini::new(); 1220 ///config.read(String::from( 1221 /// "[section] 1222 /// updog=whatsupdog")); 1223 ///config.remove_section("section"); // this will return a cloned hashmap of the stored property 1224 ///assert!(config.get_map_ref().is_empty()); // with the last section removed, our map is now empty! 1225 ///``` 1226 ///Returns `Some(section_map)` if the section exists or else, `None`. remove_section(&mut self, section: &str) -> Option<Map<String, Option<String>>>1227 pub fn remove_section(&mut self, section: &str) -> Option<Map<String, Option<String>>> { 1228 let section = if self.case_sensitive { 1229 section.to_owned() 1230 } else { 1231 section.to_lowercase() 1232 }; 1233 #[cfg(not(feature = "indexmap"))] 1234 { 1235 self.map.remove(§ion) 1236 } 1237 #[cfg(feature = "indexmap")] 1238 { 1239 self.map.swap_remove(§ion) 1240 } 1241 } 1242 1243 ///Removes a key from a section in the hashmap, returning the value attached to the key if it was previously in the map. 1244 ///## Example 1245 ///```rust 1246 ///use configparser::ini::Ini; 1247 /// 1248 ///let mut config = Ini::new(); 1249 ///config.read(String::from( 1250 /// "[section] 1251 /// updog=whatsupdog 1252 /// [anothersection] 1253 /// updog=differentdog")); 1254 ///let val = config.remove_key("anothersection", "updog").unwrap().unwrap(); 1255 ///assert_eq!(val, String::from("differentdog")); // with the last section removed, our map is now empty! 1256 ///``` 1257 ///Returns `Some(Option<String>)` if the value exists or else, `None`. remove_key(&mut self, section: &str, key: &str) -> Option<Option<String>>1258 pub fn remove_key(&mut self, section: &str, key: &str) -> Option<Option<String>> { 1259 let (section, key) = self.autocase(section, key); 1260 #[cfg(not(feature = "indexmap"))] 1261 { 1262 self.map.get_mut(§ion)?.remove(&key) 1263 } 1264 #[cfg(feature = "indexmap")] 1265 { 1266 self.map.get_mut(§ion)?.swap_remove(&key) 1267 } 1268 } 1269 } 1270 1271 #[cfg(feature = "async-std")] 1272 impl Ini { 1273 ///Loads a file asynchronously from a defined path, parses it and puts the hashmap into our struct. 1274 ///At one time, it only stores one configuration, so each call to `load()` or `read()` will clear the existing `Map`, if present. 1275 /// 1276 ///Usage is similar to `load`, but `.await` must be called after along with the usual async rules. 1277 /// 1278 ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`. 1279 ///Use `get_mut_map()` if you want a mutable reference. load_async<T: AsRef<Path>>( &mut self, path: T, ) -> Result<Map<String, Map<String, Option<String>>>, String>1280 pub async fn load_async<T: AsRef<Path>>( 1281 &mut self, 1282 path: T, 1283 ) -> Result<Map<String, Map<String, Option<String>>>, String> { 1284 self.map = match self.parse(match async_fs::read_to_string(&path).await { 1285 Err(why) => { 1286 return Err(format!( 1287 "couldn't read {}: {}", 1288 &path.as_ref().display(), 1289 why 1290 )) 1291 } 1292 Ok(s) => s, 1293 }) { 1294 Err(why) => { 1295 return Err(format!( 1296 "couldn't read {}: {}", 1297 &path.as_ref().display(), 1298 why 1299 )) 1300 } 1301 Ok(map) => map, 1302 }; 1303 Ok(self.map.clone()) 1304 } 1305 1306 ///Loads a file from a defined path, parses it and applies it to the existing hashmap in our struct. 1307 ///While `load_async()` will clear the existing `Map`, `load_and_append_async()` applies the new values on top 1308 ///of the existing hashmap, preserving previous values. 1309 /// 1310 ///Usage is similar to `load_and_append`, but `.await` must be called after along with the usual async rules. 1311 /// 1312 ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`. 1313 ///Use `get_mut_map()` if you want a mutable reference. load_and_append_async<T: AsRef<Path>>( &mut self, path: T, ) -> Result<Map<String, Map<String, Option<String>>>, String>1314 pub async fn load_and_append_async<T: AsRef<Path>>( 1315 &mut self, 1316 path: T, 1317 ) -> Result<Map<String, Map<String, Option<String>>>, String> { 1318 let loaded = match self.parse(match async_fs::read_to_string(&path).await { 1319 Err(why) => { 1320 return Err(format!( 1321 "couldn't read {}: {}", 1322 &path.as_ref().display(), 1323 why 1324 )) 1325 } 1326 Ok(s) => s, 1327 }) { 1328 Err(why) => { 1329 return Err(format!( 1330 "couldn't read {}: {}", 1331 &path.as_ref().display(), 1332 why 1333 )) 1334 } 1335 Ok(map) => map, 1336 }; 1337 1338 for (section, section_map) in loaded.iter() { 1339 self.map 1340 .entry(section.clone()) 1341 .or_insert_with(Map::new) 1342 .extend(section_map.clone()); 1343 } 1344 1345 Ok(self.map.clone()) 1346 } 1347 1348 ///Writes the current configuation to the specified path asynchronously using default formatting. If a file is not present, it is automatically created for you, if a file already 1349 ///exists, it is truncated and the configuration is written to it. 1350 /// 1351 ///Usage is the same as `write`, but `.await` must be called after along with the usual async rules. 1352 /// 1353 ///Returns a `std::io::Result<()>` type dependent on whether the write was successful or not. write_async<T: AsRef<Path>>(&self, path: T) -> std::io::Result<()>1354 pub async fn write_async<T: AsRef<Path>>(&self, path: T) -> std::io::Result<()> { 1355 async_fs::write(path.as_ref(), self.unparse(&WriteOptions::default())).await 1356 } 1357 1358 ///Writes the current configuation to the specified path asynchronously using the given formatting options. If a file is not present, it is automatically created for you, if a file already 1359 ///exists, it is truncated and the configuration is written to it. 1360 /// 1361 ///Usage is the same as `pretty_pretty_write`, but `.await` must be called after along with the usual async rules. 1362 /// 1363 ///Returns a `std::io::Result<()>` type dependent on whether the write was successful or not. pretty_write_async<T: AsRef<Path>>( &self, path: T, write_options: &WriteOptions, ) -> std::io::Result<()>1364 pub async fn pretty_write_async<T: AsRef<Path>>( 1365 &self, 1366 path: T, 1367 write_options: &WriteOptions, 1368 ) -> std::io::Result<()> { 1369 async_fs::write(path.as_ref(), self.unparse(write_options)).await 1370 } 1371 } 1372