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