• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&section)?.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(&section) {
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(&section) {
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(&section) {
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(&section) {
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(&section) {
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(&section) {
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(&section, &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(&section)
1236         }
1237         #[cfg(feature = "indexmap")]
1238         {
1239             self.map.swap_remove(&section)
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(&section)?.remove(&key)
1263         }
1264         #[cfg(feature = "indexmap")]
1265         {
1266             self.map.get_mut(&section)?.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