Coverage Report

Created: 2025-10-29 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/configparser-3.1.0/src/ini.rs
Line
Count
Source
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 {
114
0
    fn default() -> Self {
115
        Self {
116
0
            default_section: "default".to_owned(),
117
0
            comment_symbols: vec![';', '#'],
118
0
            inline_comment_symbols: None,
119
0
            delimiters: vec!['=', ':'],
120
            multiline: false,
121
            boolean_values: [
122
                (
123
                    true,
124
0
                    ["true", "yes", "t", "y", "on", "1"]
125
0
                        .iter()
126
0
                        .map(|&s| s.to_owned())
127
0
                        .collect(),
128
                ),
129
                (
130
                    false,
131
0
                    ["false", "no", "f", "n", "off", "0"]
132
0
                        .iter()
133
0
                        .map(|&s| s.to_owned())
134
0
                        .collect(),
135
                ),
136
            ]
137
0
            .iter()
138
0
            .cloned()
139
0
            .collect(),
140
            case_sensitive: false,
141
        }
142
0
    }
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 {
186
0
    fn default() -> Self {
187
0
        Self {
188
0
            space_around_delimiters: false,
189
0
            multiline_line_indentation: 4,
190
0
            blank_lines_between_sections: 0,
191
0
        }
192
0
    }
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.
207
0
    pub fn new() -> WriteOptions {
208
0
        WriteOptions::default()
209
0
    }
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.
222
0
    pub fn new_with_params(
223
0
        space_around_delimiters: bool,
224
0
        multiline_line_indentation: usize,
225
0
        blank_lines_between_sections: usize,
226
0
    ) -> WriteOptions {
227
0
        Self {
228
0
            space_around_delimiters,
229
0
            multiline_line_indentation,
230
0
            blank_lines_between_sections,
231
0
        }
232
0
    }
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.
255
0
    pub fn new() -> Ini {
256
0
        Ini::new_from_defaults(IniDefault::default())
257
0
    }
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.
268
0
    pub fn new_cs() -> Ini {
269
0
        Ini::new_from_defaults(IniDefault {
270
0
            case_sensitive: true,
271
0
            ..Default::default()
272
0
        })
273
0
    }
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
    ///```
290
0
    pub fn new_from_defaults(defaults: IniDefault) -> Ini {
291
0
        Ini {
292
0
            map: Map::new(),
293
0
            default_section: defaults.default_section,
294
0
            comment_symbols: defaults.comment_symbols,
295
0
            inline_comment_symbols: defaults.inline_comment_symbols,
296
0
            delimiters: defaults.delimiters,
297
0
            boolean_values: defaults.boolean_values,
298
0
            case_sensitive: defaults.case_sensitive,
299
0
            multiline: defaults.multiline,
300
0
        }
301
0
    }
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.
312
0
    pub fn defaults(&self) -> IniDefault {
313
0
        IniDefault {
314
0
            default_section: self.default_section.to_owned(),
315
0
            comment_symbols: self.comment_symbols.to_owned(),
316
0
            inline_comment_symbols: self.inline_comment_symbols.to_owned(),
317
0
            delimiters: self.delimiters.to_owned(),
318
0
            boolean_values: self.boolean_values.to_owned(),
319
0
            case_sensitive: self.case_sensitive,
320
0
            multiline: self.multiline,
321
0
        }
322
0
    }
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.
339
0
    pub fn load_defaults(&mut self, defaults: IniDefault) {
340
0
        self.default_section = defaults.default_section;
341
0
        self.comment_symbols = defaults.comment_symbols;
342
0
        self.inline_comment_symbols = defaults.inline_comment_symbols;
343
0
        self.delimiters = defaults.delimiters;
344
0
        self.boolean_values = defaults.boolean_values;
345
0
        self.case_sensitive = defaults.case_sensitive;
346
0
    }
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.
360
0
    pub fn set_default_section(&mut self, section: &str) {
361
0
        self.default_section = section.to_owned();
362
0
    }
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.
375
0
    pub fn set_comment_symbols(&mut self, symlist: &[char]) {
376
0
        self.comment_symbols = symlist.to_vec();
377
0
    }
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.
390
0
    pub fn set_inline_comment_symbols(&mut self, symlist: Option<&[char]>) {
391
0
        self.inline_comment_symbols = symlist.map(|val| val.to_vec());
392
0
    }
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.
405
0
    pub fn set_multiline(&mut self, multiline: bool) {
406
0
        self.multiline = multiline;
407
0
    }
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>`.
419
0
    pub fn sections(&self) -> Vec<String> {
420
0
        self.map.keys().cloned().collect()
421
0
    }
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.
436
0
    pub fn load<T: AsRef<Path>>(
437
0
        &mut self,
438
0
        path: T,
439
0
    ) -> Result<Map<String, Map<String, Option<String>>>, String> {
440
0
        self.map = match self.parse(match fs::read_to_string(&path) {
441
0
            Err(why) => {
442
0
                return Err(format!(
443
0
                    "couldn't read {}: {}",
444
0
                    &path.as_ref().display(),
445
0
                    why
446
0
                ))
447
            }
448
0
            Ok(s) => s,
449
        }) {
450
0
            Err(why) => {
451
0
                return Err(format!(
452
0
                    "couldn't read {}: {}",
453
0
                    &path.as_ref().display(),
454
0
                    why
455
0
                ))
456
            }
457
0
            Ok(map) => map,
458
        };
459
0
        Ok(self.map.clone())
460
0
    }
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.
479
0
    pub fn load_and_append<T: AsRef<Path>>(
480
0
        &mut self,
481
0
        path: T,
482
0
    ) -> Result<Map<String, Map<String, Option<String>>>, String> {
483
0
        let loaded = match self.parse(match fs::read_to_string(&path) {
484
0
            Err(why) => {
485
0
                return Err(format!(
486
0
                    "couldn't read {}: {}",
487
0
                    &path.as_ref().display(),
488
0
                    why
489
0
                ))
490
            }
491
0
            Ok(s) => s,
492
        }) {
493
0
            Err(why) => {
494
0
                return Err(format!(
495
0
                    "couldn't read {}: {}",
496
0
                    &path.as_ref().display(),
497
0
                    why
498
0
                ))
499
            }
500
0
            Ok(map) => map,
501
        };
502
503
0
        for (section, section_map) in loaded.iter() {
504
0
            self.map
505
0
                .entry(section.clone())
506
0
                .or_default()
507
0
                .extend(section_map.clone());
508
0
        }
509
510
0
        Ok(self.map.clone())
511
0
    }
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.
531
0
    pub fn read(
532
0
        &mut self,
533
0
        input: String,
534
0
    ) -> Result<Map<String, Map<String, Option<String>>>, String> {
535
0
        self.map = match self.parse(input) {
536
0
            Err(why) => return Err(why),
537
0
            Ok(map) => map,
538
        };
539
0
        Ok(self.map.clone())
540
0
    }
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.
569
0
    pub fn read_and_append(
570
0
        &mut self,
571
0
        input: String,
572
0
    ) -> Result<Map<String, Map<String, Option<String>>>, String> {
573
0
        let loaded = match self.parse(input) {
574
0
            Err(why) => return Err(why),
575
0
            Ok(map) => map,
576
        };
577
578
0
        for (section, section_map) in loaded.iter() {
579
0
            self.map
580
0
                .entry(section.clone())
581
0
                .or_default()
582
0
                .extend(section_map.clone());
583
0
        }
584
585
0
        Ok(self.map.clone())
586
0
    }
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.
603
0
    pub fn write<T: AsRef<Path>>(&self, path: T) -> std::io::Result<()> {
604
0
        fs::write(path.as_ref(), self.unparse(&WriteOptions::default()))
605
0
    }
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.
627
0
    pub fn pretty_write<T: AsRef<Path>>(
628
0
        &self,
629
0
        path: T,
630
0
        write_options: &WriteOptions,
631
0
    ) -> std::io::Result<()> {
632
0
        fs::write(path.as_ref(), self.unparse(write_options))
633
0
    }
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.
648
0
    pub fn writes(&self) -> String {
649
0
        self.unparse(&WriteOptions::default())
650
0
    }
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.
670
0
    pub fn pretty_writes(&self, write_options: &WriteOptions) -> String {
671
0
        self.unparse(write_options)
672
0
    }
673
674
    ///Private function that converts the currently stored configuration into a valid ini-syntax string.
675
0
    fn unparse(&self, write_options: &WriteOptions) -> String {
676
        // push key/value pairs in outmap to out string.
677
0
        fn unparse_key_values(
678
0
            out: &mut String,
679
0
            outmap: &Map<String, Option<String>>,
680
0
            multiline: bool,
681
0
            space_around_delimiters: bool,
682
0
            indent: usize,
683
0
        ) {
684
0
            let delimiter = if space_around_delimiters { " = " } else { "=" };
685
0
            for (key, val) in outmap.iter() {
686
0
                out.push_str(key);
687
688
0
                if let Some(value) = val {
689
0
                    if value.is_empty() {
690
0
                        out.push_str(delimiter.trim_end());
691
0
                    } else {
692
0
                        out.push_str(delimiter);
693
0
                    }
694
695
0
                    if multiline {
696
0
                        let mut lines = value.lines();
697
698
0
                        out.push_str(lines.next().unwrap_or_default());
699
700
0
                        for line in lines {
701
0
                            out.push_str(LINE_ENDING);
702
0
                            if !line.is_empty() {
703
0
                                out.push_str(" ".repeat(indent).as_ref());
704
0
                                out.push_str(line);
705
0
                            }
706
                        }
707
0
                    } else {
708
0
                        out.push_str(value);
709
0
                    }
710
0
                }
711
712
0
                out.push_str(LINE_ENDING);
713
            }
714
0
        }
715
716
0
        let line_endings = LINE_ENDING.repeat(write_options.blank_lines_between_sections);
717
0
        let mut out = String::new();
718
719
0
        if let Some(defaultmap) = self.map.get(&self.default_section) {
720
0
            unparse_key_values(
721
0
                &mut out,
722
0
                defaultmap,
723
0
                self.multiline,
724
0
                write_options.space_around_delimiters,
725
0
                write_options.multiline_line_indentation,
726
0
            );
727
0
        }
728
729
0
        let mut is_first = true;
730
0
        for (section, secmap) in self.map.iter() {
731
0
            if !is_first {
732
0
                out.push_str(line_endings.as_ref());
733
0
            }
734
0
            if section != &self.default_section {
735
0
                write!(out, "[{}]", section).unwrap();
736
0
                out.push_str(LINE_ENDING);
737
0
                unparse_key_values(
738
0
                    &mut out,
739
0
                    secmap,
740
0
                    self.multiline,
741
0
                    write_options.space_around_delimiters,
742
0
                    write_options.multiline_line_indentation,
743
0
                );
744
0
            }
745
0
            is_first = false;
746
        }
747
0
        out
748
0
    }
749
750
    ///Private function that parses ini-style syntax into a Map.
751
0
    fn parse(&self, input: String) -> Result<Map<String, Map<String, Option<String>>>, String> {
752
0
        let inline_comment_symbols: &[char] = self
753
0
            .inline_comment_symbols
754
0
            .as_deref()
755
0
            .unwrap_or_else(|| self.comment_symbols.as_ref());
756
0
        let mut map: Map<String, Map<String, Option<String>>> = Map::new();
757
0
        let mut section = self.default_section.clone();
758
0
        let mut current_key: Option<String> = None;
759
760
0
        let caser = |val: &str| {
761
0
            if self.case_sensitive {
762
0
                val.to_owned()
763
            } else {
764
0
                val.to_lowercase()
765
            }
766
0
        };
767
768
        // Track blank lines to preserve them in multiline values.
769
0
        let mut blank_lines = 0usize;
770
771
0
        for (num, raw_line) in input.lines().enumerate() {
772
0
            let line = raw_line.trim();
773
774
            // If the line is _just_ a comment, skip it entirely.
775
0
            let line = match line.find(|c: char| self.comment_symbols.contains(&c)) {
776
0
                Some(0) => continue,
777
0
                Some(_) | None => line,
778
            };
779
780
0
            let line = line.trim();
781
782
            // Skip empty lines, but keep track of them for multiline values.
783
0
            if line.is_empty() {
784
0
                blank_lines += 1;
785
0
                continue;
786
0
            }
787
788
0
            let line = match line.find(|c: char| inline_comment_symbols.contains(&c)) {
789
0
                Some(idx) => &line[..idx],
790
0
                None => line,
791
            };
792
793
0
            let trimmed = line.trim();
794
795
0
            match (trimmed.find('['), trimmed.rfind(']')) {
796
0
                (Some(0), Some(end)) => {
797
0
                    section = caser(trimmed[1..end].trim());
798
799
0
                    map.entry(section.clone()).or_default();
800
801
0
                    continue;
802
                }
803
                (Some(0), None) => {
804
0
                    return Err(format!(
805
0
                        "line {}: Found opening bracket for section name but no closing bracket",
806
0
                        num
807
0
                    ));
808
                }
809
0
                _ => {}
810
            }
811
812
0
            if raw_line.starts_with(char::is_whitespace) && self.multiline {
813
0
                let key = match current_key.as_ref() {
814
0
                    Some(x) => x,
815
                    None => {
816
0
                        return Err(format!(
817
0
                            "line {}: Started with indentation but there is no current entry",
818
0
                            num,
819
0
                        ))
820
                    }
821
                };
822
823
0
                let valmap = map.entry(section.clone()).or_default();
824
825
0
                let val = valmap
826
0
                    .entry(key.clone())
827
0
                    .or_insert_with(|| Some(String::new()));
828
829
0
                match val {
830
0
                    Some(s) => {
831
0
                        for _ in 0..blank_lines {
832
0
                            s.push_str(LINE_ENDING);
833
0
                        }
834
0
                        s.push_str(LINE_ENDING);
835
0
                        s.push_str(trimmed);
836
                    }
837
                    None => {
838
0
                        let mut s = String::with_capacity(
839
0
                            (blank_lines + 1) * LINE_ENDING.len() + trimmed.len(),
840
                        );
841
0
                        for _ in 0..blank_lines {
842
0
                            s.push_str(LINE_ENDING);
843
0
                        }
844
0
                        s.push_str(LINE_ENDING);
845
0
                        s.push_str(trimmed);
846
0
                        *val = Some(s);
847
                    }
848
                }
849
            } else {
850
0
                let valmap = map.entry(section.clone()).or_default();
851
852
0
                match trimmed.find(&self.delimiters[..]) {
853
0
                    Some(delimiter) => {
854
0
                        let key = caser(trimmed[..delimiter].trim());
855
856
0
                        if key.is_empty() {
857
0
                            return Err(format!("line {}:{}: Key cannot be empty", num, delimiter));
858
0
                        } else {
859
0
                            current_key = Some(key.clone());
860
0
861
0
                            let value = trimmed[delimiter + 1..].trim().to_owned();
862
0
863
0
                            valmap.insert(key, Some(value));
864
0
                        }
865
                    }
866
0
                    None => {
867
0
                        let key = caser(trimmed);
868
0
                        current_key = Some(key.clone());
869
0
870
0
                        valmap.insert(key, None);
871
0
                    }
872
                }
873
            }
874
875
0
            blank_lines = 0;
876
        }
877
878
0
        Ok(map)
879
0
    }
880
881
    ///Private function that cases things automatically depending on the set variable.
882
0
    fn autocase(&self, section: &str, key: &str) -> (String, String) {
883
0
        if self.case_sensitive {
884
0
            (section.to_owned(), key.to_owned())
885
        } else {
886
0
            (section.to_lowercase(), key.to_lowercase())
887
        }
888
0
    }
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`.
904
0
    pub fn get(&self, section: &str, key: &str) -> Option<String> {
905
0
        let (section, key) = self.autocase(section, key);
906
0
        self.map.get(&section)?.get(&key)?.clone()
907
0
    }
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)`.
922
0
    pub fn getbool(&self, section: &str, key: &str) -> Result<Option<bool>, String> {
923
0
        let (section, key) = self.autocase(section, key);
924
0
        match self.map.get(&section) {
925
0
            Some(secmap) => match secmap.get(&key) {
926
0
                Some(val) => match val {
927
0
                    Some(inner) => match inner.to_lowercase().parse::<bool>() {
928
0
                        Err(why) => Err(why.to_string()),
929
0
                        Ok(boolean) => Ok(Some(boolean)),
930
                    },
931
0
                    None => Ok(None),
932
                },
933
0
                None => Ok(None),
934
            },
935
0
            None => Ok(None),
936
        }
937
0
    }
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)`.
953
0
    pub fn getboolcoerce(&self, section: &str, key: &str) -> Result<Option<bool>, String> {
954
0
        let (section, key) = self.autocase(section, key);
955
0
        match self.map.get(&section) {
956
0
            Some(secmap) => match secmap.get(&key) {
957
0
                Some(val) => match val {
958
0
                    Some(inner) => {
959
0
                        let boolval = &inner.to_lowercase()[..];
960
0
                        if self
961
0
                            .boolean_values
962
0
                            .get(&true)
963
0
                            .unwrap()
964
0
                            .iter()
965
0
                            .any(|elem| elem == boolval)
966
                        {
967
0
                            Ok(Some(true))
968
0
                        } else if self
969
0
                            .boolean_values
970
0
                            .get(&false)
971
0
                            .unwrap()
972
0
                            .iter()
973
0
                            .any(|elem| elem == boolval)
974
                        {
975
0
                            Ok(Some(false))
976
                        } else {
977
0
                            Err(format!(
978
0
                                "Unable to parse value into bool at {}:{}",
979
0
                                section, key
980
0
                            ))
981
                        }
982
                    }
983
0
                    None => Ok(None),
984
                },
985
0
                None => Ok(None),
986
            },
987
0
            None => Ok(None),
988
        }
989
0
    }
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)`.
1003
0
    pub fn getint(&self, section: &str, key: &str) -> Result<Option<i64>, String> {
1004
0
        let (section, key) = self.autocase(section, key);
1005
0
        match self.map.get(&section) {
1006
0
            Some(secmap) => match secmap.get(&key) {
1007
0
                Some(val) => match val {
1008
0
                    Some(inner) => match inner.parse::<i64>() {
1009
0
                        Err(why) => Err(why.to_string()),
1010
0
                        Ok(int) => Ok(Some(int)),
1011
                    },
1012
0
                    None => Ok(None),
1013
                },
1014
0
                None => Ok(None),
1015
            },
1016
0
            None => Ok(None),
1017
        }
1018
0
    }
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)`.
1032
0
    pub fn getuint(&self, section: &str, key: &str) -> Result<Option<u64>, String> {
1033
0
        let (section, key) = self.autocase(section, key);
1034
0
        match self.map.get(&section) {
1035
0
            Some(secmap) => match secmap.get(&key) {
1036
0
                Some(val) => match val {
1037
0
                    Some(inner) => match inner.parse::<u64>() {
1038
0
                        Err(why) => Err(why.to_string()),
1039
0
                        Ok(uint) => Ok(Some(uint)),
1040
                    },
1041
0
                    None => Ok(None),
1042
                },
1043
0
                None => Ok(None),
1044
            },
1045
0
            None => Ok(None),
1046
        }
1047
0
    }
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)`.
1061
0
    pub fn getfloat(&self, section: &str, key: &str) -> Result<Option<f64>, String> {
1062
0
        let (section, key) = self.autocase(section, key);
1063
0
        match self.map.get(&section) {
1064
0
            Some(secmap) => match secmap.get(&key) {
1065
0
                Some(val) => match val {
1066
0
                    Some(inner) => match inner.parse::<f64>() {
1067
0
                        Err(why) => Err(why.to_string()),
1068
0
                        Ok(float) => Ok(Some(float)),
1069
                    },
1070
0
                    None => Ok(None),
1071
                },
1072
0
                None => Ok(None),
1073
            },
1074
0
            None => Ok(None),
1075
        }
1076
0
    }
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`.
1092
0
    pub fn get_map(&self) -> Option<Map<String, Map<String, Option<String>>>> {
1093
0
        if self.map.is_empty() {
1094
0
            None
1095
        } else {
1096
0
            Some(self.map.clone())
1097
        }
1098
0
    }
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.
1114
0
    pub fn get_map_ref(&self) -> &Map<String, Map<String, Option<String>>> {
1115
0
        &self.map
1116
0
    }
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.
1132
0
    pub fn get_mut_map(&mut self) -> &mut Map<String, Map<String, Option<String>>> {
1133
0
        &mut self.map
1134
0
    }
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.
1153
0
    pub fn set(
1154
0
        &mut self,
1155
0
        section: &str,
1156
0
        key: &str,
1157
0
        value: Option<String>,
1158
0
    ) -> Option<Option<String>> {
1159
0
        let (section, key) = self.autocase(section, key);
1160
0
        match self.map.get_mut(&section) {
1161
0
            Some(secmap) => secmap.insert(key, value),
1162
            None => {
1163
0
                let mut valmap: Map<String, Option<String>> = Map::new();
1164
0
                valmap.insert(key, value);
1165
0
                self.map.insert(section, valmap);
1166
0
                None
1167
            }
1168
        }
1169
0
    }
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.
1187
0
    pub fn setstr(
1188
0
        &mut self,
1189
0
        section: &str,
1190
0
        key: &str,
1191
0
        value: Option<&str>,
1192
0
    ) -> Option<Option<String>> {
1193
0
        let (section, key) = self.autocase(section, key);
1194
0
        self.set(&section, &key, value.map(String::from))
1195
0
    }
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.
1210
0
    pub fn clear(&mut self) {
1211
0
        self.map.clear();
1212
0
    }
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`.
1227
0
    pub fn remove_section(&mut self, section: &str) -> Option<Map<String, Option<String>>> {
1228
0
        let section = if self.case_sensitive {
1229
0
            section.to_owned()
1230
        } else {
1231
0
            section.to_lowercase()
1232
        };
1233
        #[cfg(not(feature = "indexmap"))]
1234
        {
1235
            self.map.remove(&section)
1236
        }
1237
        #[cfg(feature = "indexmap")]
1238
        {
1239
0
            self.map.swap_remove(&section)
1240
        }
1241
0
    }
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`.
1258
0
    pub fn remove_key(&mut self, section: &str, key: &str) -> Option<Option<String>> {
1259
0
        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
0
            self.map.get_mut(&section)?.swap_remove(&key)
1267
        }
1268
0
    }
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.
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.
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.
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.
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
}