//!The ini module provides all the things necessary to load and parse ini-syntax files. The most important of which is the `Ini` struct.
//!See the [implementation](https://docs.rs/configparser/*/configparser/ini/struct.Ini.html) documentation for more details.
#[cfg(feature = "indexmap")]
use indexmap::IndexMap as Map;
#[cfg(feature = "serde")]
use serde::de::{Deserialize, Deserializer};
#[cfg(feature = "serde")]
use serde::ser::{Serialize, Serializer};
#[cfg(not(feature = "indexmap"))]
use std::collections::HashMap as Map;
#[cfg(feature = "tokio")]
use tokio::fs as async_fs;

use std::collections::HashMap;
use std::convert::AsRef;
use std::fmt::Write;
use std::fs;
use std::path::Path;

///The `Ini` struct simply contains a nested hashmap of the loaded configuration, the default section header and comment symbols.
///## Example
///```rust
///use configparser::ini::Ini;
///
///let mut config = Ini::new();
///```
#[derive(Debug, Clone, Eq, PartialEq, Default)]
#[non_exhaustive]
pub struct Ini {
    map: Map<String, Map<String, Option<String>>>,
    default_section: std::string::String,
    comment_symbols: Vec<char>,
    inline_comment_symbols: Option<Vec<char>>,
    delimiters: Vec<char>,
    boolean_values: HashMap<bool, Vec<String>>,
    case_sensitive: bool,
    multiline: bool,
    enable_inline_comments: bool,
    cascade_defaults: bool,
}

#[cfg(all(feature = "serde", not(feature = "indexmap")))]
impl Serialize for Ini {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        // Delegate serialization to the internal map only
        self.map.serialize(serializer)
    }
}

#[cfg(all(feature = "serde", not(feature = "indexmap")))]
impl<'de> Deserialize<'de> for Ini {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        // First, deserialize the raw map
        let map = Map::<String, Map<String, Option<String>>>::deserialize(deserializer)
            .map_err(serde::de::Error::custom)?;
        // Build an Ini with defaults, then replace its map
        let mut ini = Ini::new();
        ini.map = map;
        Ok(ini)
    }
}

#[cfg(all(feature = "serde", feature = "indexmap"))]
impl Serialize for Ini {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        // Delegate to IndexMap’s Serialize impl
        serde::Serialize::serialize(&self.map, serializer)
    }
}

#[cfg(all(feature = "serde", feature = "indexmap"))]
impl<'de> Deserialize<'de> for Ini {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let map = serde::Deserialize::deserialize(deserializer)?;
        let mut ini = Ini::new();
        ini.map = map;
        Ok(ini)
    }
}

///The `IniDefault` struct serves as a template to create other `Ini` objects from. It can be used to store and load
///default properties from different `Ini` objects.
///## Example
///```rust
///use configparser::ini::Ini;
///
///let mut config = Ini::new();
///let default = config.defaults();
///let mut config2 = Ini::new_from_defaults(default); // default gets consumed
///```
#[derive(Debug, Clone, Eq, PartialEq)]
#[non_exhaustive]
pub struct IniDefault {
    ///Denotes the default section header name.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///let default = config.defaults();
    ///assert_eq!(default.default_section, "default");
    ///```
    pub default_section: std::string::String,
    ///Denotes the set comment symbols for the object.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///let default = config.defaults();
    ///assert_eq!(default.comment_symbols, vec![';', '#']);
    ///```
    pub comment_symbols: Vec<char>,
    ///Denotes the set of inline comment symbols for the object. The default of
    ///`None` means to fall back to the normal comment symbols.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///let default = config.defaults();
    ///assert_eq!(default.inline_comment_symbols, None);
    ///```
    pub inline_comment_symbols: Option<Vec<char>>,
    ///Denotes the set delimiters for the key-value pairs.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///let default = config.defaults();
    ///assert_eq!(default.delimiters, vec!['=', ':']);
    ///```
    pub delimiters: Vec<char>,
    pub boolean_values: HashMap<bool, Vec<String>>,
    ///Denotes if the `Ini` object is case-sensitive.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///let default = config.defaults();
    ///assert_eq!(default.case_sensitive, false);
    ///```
    pub case_sensitive: bool,
    ///Denotes if the `Ini` object parses multiline strings.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///let default = config.defaults();
    ///assert_eq!(default.multiline, false);
    ///```
    pub multiline: bool,
    ///Denotes if the `Ini` object recognizes inline comments.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///let default = config.defaults();
    ///assert_eq!(default.enable_inline_comments, true);
    ///```
    pub enable_inline_comments: bool,
    ///Cascade defaults automatically.  If a key is not defined in a get*()
    ///call, but exists in the [DEFAULT] section, the value in the [DEFAULT]
    ///section will be returned.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///let default = config.defaults();
    ///assert_eq!(default.cascade_defaults, false);
    ///```
    pub cascade_defaults: bool,
}

impl Default for IniDefault {
    fn default() -> Self {
        Self {
            default_section: "default".to_owned(),
            comment_symbols: vec![';', '#'],
            inline_comment_symbols: None,
            delimiters: vec!['=', ':'],
            multiline: false,
            boolean_values: [
                (
                    true,
                    ["true", "yes", "t", "y", "on", "1"]
                        .iter()
                        .map(|&s| s.to_owned())
                        .collect(),
                ),
                (
                    false,
                    ["false", "no", "f", "n", "off", "0"]
                        .iter()
                        .map(|&s| s.to_owned())
                        .collect(),
                ),
            ]
            .iter()
            .cloned()
            .collect(),
            case_sensitive: false,
            enable_inline_comments: true, // retain compatibility with previous versions
            cascade_defaults: false,      // retain backwards compatibility
        }
    }
}

/// Use this struct to define formatting options for the `pretty_write` functions.
#[derive(Debug, Clone, Eq, PartialEq)]
#[non_exhaustive]
pub struct WriteOptions {
    ///If true then the keys and values will be separated by " = ". In the special case where the value is empty, the
    ///line ends with " =".
    ///If false then keys and values will be separated by "=".
    ///Default is `false`.
    ///## Example
    ///```rust
    ///use configparser::ini::WriteOptions;
    ///
    ///let mut write_options = WriteOptions::default();
    ///assert_eq!(write_options.space_around_delimiters, false);
    ///```
    pub space_around_delimiters: bool,

    ///Defines the number of spaces for indentation of for multiline values.
    ///Default is 4 spaces.
    ///## Example
    ///```rust
    ///use configparser::ini::WriteOptions;
    ///
    ///let mut write_options = WriteOptions::default();
    ///assert_eq!(write_options.multiline_line_indentation, 4);
    ///```
    pub multiline_line_indentation: usize,

    ///Defines the number of blank lines between sections.
    ///Default is 0.
    ///## Example
    ///```rust
    ///use configparser::ini::WriteOptions;
    ///
    ///let mut write_options = WriteOptions::default();
    ///assert_eq!(write_options.blank_lines_between_sections, 0);
    ///```
    pub blank_lines_between_sections: usize,
}

impl Default for WriteOptions {
    fn default() -> Self {
        Self {
            space_around_delimiters: false,
            multiline_line_indentation: 4,
            blank_lines_between_sections: 0,
        }
    }
}

impl WriteOptions {
    ///Creates a new `WriteOptions` object with the default values.
    ///## Example
    ///```rust
    ///use configparser::ini::WriteOptions;
    ///
    ///let write_options = WriteOptions::new();
    ///assert_eq!(write_options.space_around_delimiters, false);
    ///assert_eq!(write_options.multiline_line_indentation, 4);
    ///assert_eq!(write_options.blank_lines_between_sections, 0);
    ///```
    ///Returns the struct and stores it in the calling variable.
    pub fn new() -> WriteOptions {
        WriteOptions::default()
    }

    ///Creates a new `WriteOptions` object with the given parameters.
    ///## Example
    ///```rust
    ///use configparser::ini::WriteOptions;
    ///
    ///let write_options = WriteOptions::new_with_params(true, 2, 1);
    ///assert_eq!(write_options.space_around_delimiters, true);
    ///assert_eq!(write_options.multiline_line_indentation, 2);
    ///assert_eq!(write_options.blank_lines_between_sections, 1);
    ///```
    ///Returns the struct and stores it in the calling variable.
    pub fn new_with_params(
        space_around_delimiters: bool,
        multiline_line_indentation: usize,
        blank_lines_between_sections: usize,
    ) -> WriteOptions {
        Self {
            space_around_delimiters,
            multiline_line_indentation,
            blank_lines_between_sections,
        }
    }
}

#[cfg(windows)]
const LINE_ENDING: &str = "\r\n";
#[cfg(not(windows))]
const LINE_ENDING: &str = "\n";

impl Ini {
    ///Creates a new `Map` of `Map<String, Map<String, Option<String>>>` type for the struct.
    ///All values in the Map are stored in `String` type.
    ///
    ///By default, [`std::collections::HashMap`] is used for the Map object.
    ///The `indexmap` feature can be used to use an [`indexmap::map::IndexMap`] instead, which
    ///allows keeping the insertion order for sections and keys.
    ///
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///```
    ///Returns the struct and stores it in the calling variable.
    pub fn new() -> Ini {
        Ini::new_from_defaults(IniDefault::default())
    }

    ///Creates a new **case-sensitive** `Map` of `Map<String, Map<String, Option<String>>>` type for the struct.
    ///All values in the Map are stored in `String` type.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new_cs();
    ///```
    ///Returns the struct and stores it in the calling variable.
    pub fn new_cs() -> Ini {
        Ini::new_from_defaults(IniDefault {
            case_sensitive: true,
            ..Default::default()
        })
    }

    ///Creates a new `Ini` with the given defaults from an existing `IniDefault` object.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///use configparser::ini::IniDefault;
    ///
    ///let mut default = IniDefault::default();
    ///default.comment_symbols = vec![';'];
    ///default.delimiters = vec!['='];
    ///let mut config = Ini::new_from_defaults(default.clone());
    ///// Now, load as usual with new defaults:
    ///let map = config.load("tests/test.ini").unwrap();
    ///assert_eq!(config.defaults(), default);
    ///
    ///```
    pub fn new_from_defaults(defaults: IniDefault) -> Ini {
        Ini {
            map: Map::new(),
            default_section: defaults.default_section,
            comment_symbols: defaults.comment_symbols,
            inline_comment_symbols: defaults.inline_comment_symbols,
            delimiters: defaults.delimiters,
            boolean_values: defaults.boolean_values,
            case_sensitive: defaults.case_sensitive,
            multiline: defaults.multiline,
            enable_inline_comments: defaults.enable_inline_comments,
            cascade_defaults: defaults.cascade_defaults,
        }
    }

    ///Fetches the defaults from the current `Ini` object and stores it as a `IniDefault` struct for usage elsewhere.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///let default = config.defaults();
    ///```
    ///Returns an `IniDefault` object. Keep in mind that it will get borrowed since it has non-`Copy` types.
    pub fn defaults(&self) -> IniDefault {
        IniDefault {
            default_section: self.default_section.to_owned(),
            comment_symbols: self.comment_symbols.to_owned(),
            inline_comment_symbols: self.inline_comment_symbols.to_owned(),
            delimiters: self.delimiters.to_owned(),
            boolean_values: self.boolean_values.to_owned(),
            case_sensitive: self.case_sensitive,
            multiline: self.multiline,
            enable_inline_comments: self.enable_inline_comments,
            cascade_defaults: self.cascade_defaults,
        }
    }

    ///Takes an `IniDefault` object and stores its properties in the calling `Ini` object. This happens in-place and
    ///does not work retroactively, only future operations are affected.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///use configparser::ini::IniDefault;
    ///
    ///let mut config = Ini::new();
    ///let mut default = IniDefault::default();
    ///default.case_sensitive = true;
    ///// This is equivalent to ini_cs() defaults
    ///config.load_defaults(default.clone());
    ///assert_eq!(config.defaults(), default);
    ///```
    ///Returns nothing.
    pub fn load_defaults(&mut self, defaults: IniDefault) {
        self.default_section = defaults.default_section;
        self.comment_symbols = defaults.comment_symbols;
        self.inline_comment_symbols = defaults.inline_comment_symbols;
        self.delimiters = defaults.delimiters;
        self.boolean_values = defaults.boolean_values;
        self.case_sensitive = defaults.case_sensitive;
    }

    ///Sets the default section header to the defined string (the default is `default`).
    ///It must be set before `load()` or `read()` is called in order to take effect.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///
    ///config.set_default_section("topsecret");
    ///let map = config.load("tests/test.ini").unwrap();
    ///```
    ///Returns nothing.
    pub fn set_default_section(&mut self, section: &str) {
        self.default_section = section.to_owned();
    }

    ///Sets the default comment symbols to the defined character slice (the defaults are `;` and `#`).
    ///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.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.set_comment_symbols(&['!', '#']);
    ///let map = config.load("tests/test.ini").unwrap();
    ///```
    ///Returns nothing.
    pub fn set_comment_symbols(&mut self, symlist: &[char]) {
        self.comment_symbols = symlist.to_vec();
    }

    ///Sets the default inline comment symbols to the defined character slice (the default is `None` which falls back to the normal comment symbols).
    ///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.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.set_inline_comment_symbols(Some(&['!', '#']));
    ///let map = config.load("tests/test.ini").unwrap();
    ///```
    ///Returns nothing.
    pub fn set_inline_comment_symbols(&mut self, symlist: Option<&[char]>) {
        self.inline_comment_symbols = symlist.map(|val| val.to_vec());
    }

    ///Sets multiline string support.
    ///It must be set before `load()` or `read()` is called in order to take effect.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.set_multiline(true);
    ///let map = config.load("tests/test.ini").unwrap();
    ///```
    ///Returns nothing.
    pub fn set_multiline(&mut self, multiline: bool) {
        self.multiline = multiline;
    }

    ///Sets the behavior around the cascading of defaults.  If this is set
    ///to `true`, a get*() call for a section without a matching key value will
    ///attempt to get that key value from the [DEFAULT] section if it exists
    ///there.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.set_cascade_defaults(true);
    ///let map = config.load("tests/test_cascade_defaults.ini").unwrap();
    ///
    ///let val = config.get("mysection", "fallback_key");  // Will fall back to the [DEFAULT] section for "key"
    ///assert_eq!(val, Some(String::from("fallback value")));
    ///```
    pub fn set_cascade_defaults(&mut self, cascade_defaults: bool) {
        self.cascade_defaults = cascade_defaults;
    }

    ///Gets all the sections of the currently-stored `Map` in a vector.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.load("tests/test.ini");
    ///let sections = config.sections();
    ///```
    ///Returns `Vec<String>`.
    pub fn sections(&self) -> Vec<String> {
        self.map.keys().cloned().collect()
    }

    ///Loads a file from a defined path, parses it and puts the hashmap into our struct.
    ///At one time, it only stores one configuration, so each call to `load()` or `read()` will clear the existing `Map`, if present.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///let map = config.load("tests/test.ini").unwrap();  // we can get a clone like this, or just store it
    /////Then, we can use standard hashmap functions like:
    ///let values = map.get("values").unwrap();
    ///```
    ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`.
    ///Use `get_mut_map()` if you want a mutable reference.
    pub fn load<T: AsRef<Path>>(
        &mut self,
        path: T,
    ) -> Result<Map<String, Map<String, Option<String>>>, String> {
        self.map = match self.parse(match fs::read_to_string(&path) {
            Err(why) => {
                return Err(format!(
                    "couldn't read {}: {}",
                    &path.as_ref().display(),
                    why
                ));
            }
            Ok(s) => s,
        }) {
            Err(why) => {
                return Err(format!(
                    "couldn't read {}: {}",
                    &path.as_ref().display(),
                    why
                ));
            }
            Ok(map) => map,
        };
        Ok(self.map.clone())
    }

    /// Loads configuration data from any stream implementing `std::io::Read`, parses it, and applies it to the internal map.
    ///
    /// This function reads the entire stream into a string, parses it as an INI file, and updates the internal map.
    /// On error, returns a descriptive message. The previous map is replaced with the new one.
    ///
    /// # Arguments
    /// * `reader` - Any type that implements `std::io::Read`, such as a file, buffer, or network stream.
    ///
    /// # Returns
    /// * `Result<Map<String, Map<String, Option<String>>>, String>` - The parsed map on success, or an error message on failure.
    ///
    /// # Example
    /// ```rust
    /// use configparser::ini::Ini;
    ///
    /// let mut config = Ini::new();
    /// let input = "[section]\nkey=value";
    /// config.load_from_stream(input.as_bytes()).unwrap();
    ///
    /// assert_eq!(config.get("section", "key").unwrap(), "value");
    /// ```
    pub fn load_from_stream<R: std::io::Read>(
        &mut self,
        mut reader: R,
    ) -> Result<Map<String, Map<String, Option<String>>>, String> {
        let mut buf = String::new();
        if let Err(err) = reader.read_to_string(&mut buf) {
            return Err(format!("couldn't read from stream: {}", err));
        }

        self.map = match self.parse(buf) {
            Err(why) => {
                return Err(format!("couldn't read from stream: {}", why));
            }
            Ok(map) => map,
        };
        Ok(self.map.clone())
    }

    ///Loads a file from a defined path, parses it and applies it to the existing hashmap in our struct.
    ///While `load()` will clear the existing `Map`, `load_and_append()` applies the new values on top of
    ///the existing hashmap, preserving previous values.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.load("tests/test.ini").unwrap();
    ///config.load_and_append("tests/sys_cfg.ini").ok();  // we don't have to worry if this doesn't succeed
    ///config.load_and_append("tests/user_cfg.ini").ok();  // we don't have to worry if this doesn't succeed
    ///let map = config.get_map().unwrap();
    /////Then, we can use standard hashmap functions like:
    ///let values = map.get("values").unwrap();
    ///```
    ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`.
    ///Use `get_mut_map()` if you want a mutable reference.
    pub fn load_and_append<T: AsRef<Path>>(
        &mut self,
        path: T,
    ) -> Result<Map<String, Map<String, Option<String>>>, String> {
        let loaded = match self.parse(match fs::read_to_string(&path) {
            Err(why) => {
                return Err(format!(
                    "couldn't read {}: {}",
                    &path.as_ref().display(),
                    why
                ));
            }
            Ok(s) => s,
        }) {
            Err(why) => {
                return Err(format!(
                    "couldn't read {}: {}",
                    &path.as_ref().display(),
                    why
                ));
            }
            Ok(map) => map,
        };

        for (section, section_map) in loaded.iter() {
            self.map
                .entry(section.clone())
                .or_default()
                .extend(section_map.clone());
        }

        Ok(self.map.clone())
    }

    ///Reads an input string, parses it and puts the hashmap into our struct.
    ///At one time, it only stores one configuration, so each call to `load()` or `read()` will clear the existing `Map`, if present.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///let map = match config.read(String::from(
    ///    "[2000s]
    ///    2020 = bad")) {
    ///    Err(why) => panic!("{}", why),
    ///    Ok(inner) => inner
    ///};
    ///let this_year = map["2000s"]["2020"].clone().unwrap();
    ///assert_eq!(this_year, "bad"); // value accessible!
    ///```
    ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`.
    ///Use `get_mut_map()` if you want a mutable reference.
    pub fn read(
        &mut self,
        input: String,
    ) -> Result<Map<String, Map<String, Option<String>>>, String> {
        self.map = self.parse(input)?;
        Ok(self.map.clone())
    }

    ///Reads an input string, parses it and applies it to the existing hashmap in our struct.
    ///While `read()` and `load()` will clear the existing `Map`, `read_and_append()` applies the new
    ///values on top of the existing hashmap, preserving previous values.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///if let Err(why) = config.read(String::from(
    ///    "[2000s]
    ///    2020 = bad
    ///    2023 = better")) {
    ///    panic!("{}", why);
    ///};
    ///if let Err(why) = config.read_and_append(String::from(
    ///    "[2000s]
    ///    2020 = terrible")) {
    ///    panic!("{}", why);
    ///};
    ///let map = config.get_map().unwrap();
    ///let few_years_ago = map["2000s"]["2020"].clone().unwrap();
    ///let this_year = map["2000s"]["2023"].clone().unwrap();
    ///assert_eq!(few_years_ago, "terrible"); // value updated!
    ///assert_eq!(this_year, "better"); // keeps old values!
    ///```
    ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`.
    ///Use `get_mut_map()` if you want a mutable reference.
    pub fn read_and_append(
        &mut self,
        input: String,
    ) -> Result<Map<String, Map<String, Option<String>>>, String> {
        let loaded = self.parse(input)?;

        for (section, section_map) in loaded.iter() {
            self.map
                .entry(section.clone())
                .or_default()
                .extend(section_map.clone());
        }

        Ok(self.map.clone())
    }

    ///Writes the current configuation to the specified path using default formatting.
    ///If a file is not present then it is automatically created for you. If a file already exists then it is overwritten.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///fn main() -> std::io::Result<()> {
    ///  let mut config = Ini::new();
    ///  config.read(String::from(
    ///    "[2000s]
    ///    2020 = bad"));
    ///  config.write("output.ini")
    ///}
    ///```
    ///Returns a `std::io::Result<()>` type dependent on whether the write was successful or not.
    pub fn write<T: AsRef<Path>>(&self, path: T) -> std::io::Result<()> {
        fs::write(path.as_ref(), self.unparse(&WriteOptions::default()))
    }

    ///Writes the current configuation to the specified path using the given formatting options.
    ///If a file is not present then it is automatically created for you. If a file already exists then it is overwritten.
    ///## Example
    ///```rust
    ///use configparser::ini::{Ini, WriteOptions};
    ///
    ///fn main() -> std::io::Result<()> {
    ///  let mut write_options = WriteOptions::default();
    ///  write_options.space_around_delimiters = true;
    ///  write_options.multiline_line_indentation = 2;
    ///  write_options.blank_lines_between_sections = 1;
    ///
    ///  let mut config = Ini::new();
    ///  config.read(String::from(
    ///    "[2000s]
    ///    2020 = bad"));
    ///  config.pretty_write("output.ini", &write_options)
    ///}
    ///```
    ///Returns a `std::io::Result<()>` type dependent on whether the write was successful or not.
    pub fn pretty_write<T: AsRef<Path>>(
        &self,
        path: T,
        write_options: &WriteOptions,
    ) -> std::io::Result<()> {
        fs::write(path.as_ref(), self.unparse(write_options))
    }

    ///Returns a string with the current configuration formatted with valid ini-syntax using default formatting.
    ///This is always safe since the configuration is validated during parsing.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.read(String::from(
    ///  "[2000s]
    ///  2020 = bad"));
    ///let outstring = config.writes();
    ///```
    ///Returns a `String` type contatining the ini-syntax file.
    pub fn writes(&self) -> String {
        self.unparse(&WriteOptions::default())
    }

    ///Returns a string with the current configuration formatted with valid ini-syntax using the given formatting options.
    ///This is always safe since the configuration is validated during parsing.
    ///## Example
    ///```rust
    ///use configparser::ini::{Ini, WriteOptions};
    ///
    ///let mut write_options = WriteOptions::default();
    ///write_options.space_around_delimiters = true;
    ///write_options.multiline_line_indentation = 2;
    ///write_options.blank_lines_between_sections = 1;
    ///
    ///let mut config = Ini::new();
    ///config.read(String::from(
    ///  "[2000s]
    ///  2020 = bad"));
    ///let outstring = config.pretty_writes(&write_options);
    ///```
    ///Returns a `String` type contatining the ini-syntax file.
    pub fn pretty_writes(&self, write_options: &WriteOptions) -> String {
        self.unparse(write_options)
    }

    ///Private function that converts the currently stored configuration into a valid ini-syntax string.
    fn unparse(&self, write_options: &WriteOptions) -> String {
        // push key/value pairs in outmap to out string.
        fn unparse_key_values(
            out: &mut String,
            outmap: &Map<String, Option<String>>,
            multiline: bool,
            space_around_delimiters: bool,
            indent: usize,
        ) {
            let delimiter = if space_around_delimiters { " = " } else { "=" };
            for (key, val) in outmap.iter() {
                out.push_str(key);

                if let Some(value) = val {
                    if value.is_empty() {
                        out.push_str(delimiter.trim_end());
                    } else {
                        out.push_str(delimiter);
                    }

                    if multiline {
                        let mut lines = value.lines();

                        out.push_str(lines.next().unwrap_or_default());

                        for line in lines {
                            out.push_str(LINE_ENDING);
                            if !line.is_empty() {
                                out.push_str(" ".repeat(indent).as_ref());
                                out.push_str(line);
                            }
                        }
                    } else {
                        out.push_str(value);
                    }
                }

                out.push_str(LINE_ENDING);
            }
        }

        let line_endings = LINE_ENDING.repeat(write_options.blank_lines_between_sections);
        let mut out = String::new();

        if let Some(defaultmap) = self.map.get(&self.default_section) {
            unparse_key_values(
                &mut out,
                defaultmap,
                self.multiline,
                write_options.space_around_delimiters,
                write_options.multiline_line_indentation,
            );
        }

        let mut is_first = true;
        for (section, secmap) in self.map.iter() {
            if !is_first {
                out.push_str(line_endings.as_ref());
            }
            if section != &self.default_section {
                write!(out, "[{}]", section).unwrap();
                out.push_str(LINE_ENDING);
                unparse_key_values(
                    &mut out,
                    secmap,
                    self.multiline,
                    write_options.space_around_delimiters,
                    write_options.multiline_line_indentation,
                );
            }
            is_first = false;
        }
        out
    }

    ///Private function that parses ini-style syntax into a Map.
    fn parse(&self, input: String) -> Result<Map<String, Map<String, Option<String>>>, String> {
        let inline_comment_symbols: &[char] = self
            .inline_comment_symbols
            .as_deref()
            .unwrap_or_else(|| self.comment_symbols.as_ref());
        let mut map: Map<String, Map<String, Option<String>>> = Map::new();
        let mut section = self.default_section.clone();
        let mut current_key: Option<String> = None;

        let caser = |val: &str| {
            if self.case_sensitive {
                val.to_owned()
            } else {
                val.to_lowercase()
            }
        };

        // Track blank lines to preserve them in multiline values.
        let mut blank_lines = 0usize;

        for (num, raw_line) in input.lines().enumerate() {
            let line = raw_line.trim();

            // If the line is _just_ a comment, skip it entirely.
            let line = match line.find(|c: char| self.comment_symbols.contains(&c)) {
                Some(0) => continue,
                Some(_) | None => line,
            };

            let line = line.trim();

            // Skip empty lines, but keep track of them for multiline values.
            if line.is_empty() {
                blank_lines += 1;
                continue;
            }

            let line = if self.enable_inline_comments {
                match line.find(|c: char| inline_comment_symbols.contains(&c)) {
                    Some(idx) => &line[..idx],
                    None => line,
                }
            } else {
                line
            };

            let trimmed = line.trim();

            match (trimmed.find('['), trimmed.rfind(']')) {
                (Some(0), Some(end)) => {
                    section = caser(trimmed[1..end].trim());

                    map.entry(section.clone()).or_default();

                    continue;
                }
                (Some(0), None) => {
                    return Err(format!(
                        "line {}: Found opening bracket for section name but no closing bracket",
                        num
                    ));
                }
                _ => {}
            }

            if raw_line.starts_with(char::is_whitespace) && self.multiline {
                let key = match current_key.as_ref() {
                    Some(x) => x,
                    None => {
                        return Err(format!(
                            "line {}: Started with indentation but there is no current entry",
                            num,
                        ));
                    }
                };

                let valmap = map.entry(section.clone()).or_default();

                let val = valmap
                    .entry(key.clone())
                    .or_insert_with(|| Some(String::new()));

                match val {
                    Some(s) => {
                        for _ in 0..blank_lines {
                            s.push_str(LINE_ENDING);
                        }
                        s.push_str(LINE_ENDING);
                        s.push_str(trimmed);
                    }
                    None => {
                        let mut s = String::with_capacity(
                            (blank_lines + 1) * LINE_ENDING.len() + trimmed.len(),
                        );
                        for _ in 0..blank_lines {
                            s.push_str(LINE_ENDING);
                        }
                        s.push_str(LINE_ENDING);
                        s.push_str(trimmed);
                        *val = Some(s);
                    }
                }
            } else {
                let valmap = map.entry(section.clone()).or_default();

                match trimmed.find(&self.delimiters[..]) {
                    Some(delimiter) => {
                        let key = caser(trimmed[..delimiter].trim());

                        if key.is_empty() {
                            return Err(format!("line {}:{}: Key cannot be empty", num, delimiter));
                        } else {
                            current_key = Some(key.clone());

                            let value = trimmed[delimiter + 1..].trim().to_owned();

                            valmap.insert(key, Some(value));
                        }
                    }
                    None => {
                        let key = caser(trimmed);
                        current_key = Some(key.clone());

                        valmap.insert(key, None);
                    }
                }
            }

            blank_lines = 0;
        }

        Ok(map)
    }

    ///Private function that cases things automatically depending on the set variable.
    fn autocase(&self, section: &str, key: &str) -> (String, String) {
        if self.case_sensitive {
            (section.to_owned(), key.to_owned())
        } else {
            (section.to_lowercase(), key.to_lowercase())
        }
    }

    ///Returns a clone of the stored value from the key stored in the defined section.
    ///Unlike accessing the map directly, `get()` can process your input to make case-insensitive access *if* the
    ///default constructor is used.
    ///All `get` functions will do this automatically under the hood.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.load("tests/test.ini");
    ///let value = config.get("default", "defaultvalues").unwrap();
    ///assert_eq!(value, String::from("defaultvalues"));
    ///```
    ///Returns `Some(value)` of type `String` if value is found or else returns `None`.
    pub fn get(&self, section: &str, key: &str) -> Option<String> {
        let (section, key) = self.autocase(section, key);
        let val = match self.map.get(&section) {
            Some(secmap) => match secmap.get(&key) {
                Some(val) => val.clone(),
                None => None,
            },
            None => None,
        };

        if val.is_none() && self.cascade_defaults {
            return self.map.get(&self.default_section)?.get(&key)?.clone();
        }

        val
    }

    ///Parses the stored value from the key stored in the defined section to a `bool`.
    ///For ease of use, the function converts the type case-insensitively (`true` == `True`).
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.load("tests/test.ini");
    ///let value = config.getbool("values", "bool").unwrap().unwrap();
    ///assert!(value);  // value accessible!
    ///```
    ///Returns `Ok(Some(value))` of type `bool` if value is found or else returns `Ok(None)`.
    ///If the parsing fails, it returns an `Err(string)`.
    pub fn getbool(&self, section: &str, key: &str) -> Result<Option<bool>, String> {
        let (section, key) = self.autocase(section, key);
        match self.map.get(&section) {
            Some(secmap) => match secmap.get(&key) {
                Some(val) => match val {
                    Some(inner) => match inner.to_lowercase().parse::<bool>() {
                        Err(why) => Err(why.to_string()),
                        Ok(boolean) => Ok(Some(boolean)),
                    },
                    None => {
                        if self.cascade_defaults && section != self.default_section {
                            return self.getbool(&self.default_section, &key);
                        }
                        Ok(None)
                    }
                },
                None => {
                    if self.cascade_defaults && section != self.default_section {
                        return self.getbool(&self.default_section, &key);
                    }
                    Ok(None)
                }
            },
            None => {
                if self.cascade_defaults && section != self.default_section {
                    return self.getbool(&self.default_section, &key);
                }
                Ok(None)
            }
        }
    }

    ///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.
    ///It attempts to case-insenstively find `true`, `yes`, `t`, `y`, `1` and `on` to parse it as `True`.
    ///Similarly it attempts to case-insensitvely find `false`, `no`, `f`, `n`, `0` and `off` to parse it as `False`.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.load("tests/test.ini");
    ///let value = config.getboolcoerce("values", "boolcoerce").unwrap().unwrap();
    ///assert!(!value);  // value accessible!
    ///```
    ///Returns `Ok(Some(value))` of type `bool` if value is found or else returns `Ok(None)`.
    ///If the parsing fails, it returns an `Err(string)`.
    pub fn getboolcoerce(&self, section: &str, key: &str) -> Result<Option<bool>, String> {
        let (section, key) = self.autocase(section, key);
        match self.map.get(&section) {
            Some(secmap) => match secmap.get(&key) {
                Some(val) => match val {
                    Some(inner) => {
                        let boolval = &inner.to_lowercase()[..];
                        if self
                            .boolean_values
                            .get(&true)
                            .unwrap()
                            .iter()
                            .any(|elem| elem == boolval)
                        {
                            Ok(Some(true))
                        } else if self
                            .boolean_values
                            .get(&false)
                            .unwrap()
                            .iter()
                            .any(|elem| elem == boolval)
                        {
                            Ok(Some(false))
                        } else {
                            Err(format!(
                                "Unable to parse value into bool at {}:{}",
                                section, key
                            ))
                        }
                    }
                    None => {
                        if self.cascade_defaults && section != self.default_section {
                            return self.getboolcoerce(&self.default_section, &key);
                        }
                        Ok(None)
                    }
                },
                None => {
                    if self.cascade_defaults && section != self.default_section {
                        return self.getboolcoerce(&self.default_section, &key);
                    }
                    Ok(None)
                }
            },
            None => {
                if self.cascade_defaults && section != self.default_section {
                    return self.getboolcoerce(&self.default_section, &key);
                }
                Ok(None)
            }
        }
    }

    ///Parses the stored value from the key stored in the defined section to an `i64`.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.load("tests/test.ini");
    ///let value = config.getint("values", "int").unwrap().unwrap();
    ///assert_eq!(value, -31415);  // value accessible!
    ///```
    ///Returns `Ok(Some(value))` of type `i64` if value is found or else returns `Ok(None)`.
    ///If the parsing fails, it returns an `Err(string)`.
    pub fn getint(&self, section: &str, key: &str) -> Result<Option<i64>, String> {
        let (section, key) = self.autocase(section, key);
        match self.map.get(&section) {
            Some(secmap) => match secmap.get(&key) {
                Some(val) => match val {
                    Some(inner) => match inner.parse::<i64>() {
                        Err(why) => Err(why.to_string()),
                        Ok(int) => Ok(Some(int)),
                    },
                    None => {
                        if self.cascade_defaults && section != self.default_section {
                            return self.getint(&self.default_section, &key);
                        }
                        Ok(None)
                    }
                },
                None => {
                    if self.cascade_defaults && section != self.default_section {
                        return self.getint(&self.default_section, &key);
                    }
                    Ok(None)
                }
            },
            None => {
                if self.cascade_defaults && section != self.default_section {
                    return self.getint(&self.default_section, &key);
                }
                Ok(None)
            }
        }
    }

    ///Parses the stored value from the key stored in the defined section to a `u64`.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.load("tests/test.ini");
    ///let value = config.getint("values", "Uint").unwrap().unwrap();
    ///assert_eq!(value, 31415);  // value accessible!
    ///```
    ///Returns `Ok(Some(value))` of type `u64` if value is found or else returns `Ok(None)`.
    ///If the parsing fails, it returns an `Err(string)`.
    pub fn getuint(&self, section: &str, key: &str) -> Result<Option<u64>, String> {
        let (section, key) = self.autocase(section, key);
        match self.map.get(&section) {
            Some(secmap) => match secmap.get(&key) {
                Some(val) => match val {
                    Some(inner) => match inner.parse::<u64>() {
                        Err(why) => Err(why.to_string()),
                        Ok(uint) => Ok(Some(uint)),
                    },
                    None => {
                        if self.cascade_defaults && section != self.default_section {
                            return self.getuint(&self.default_section, &key);
                        }
                        Ok(None)
                    }
                },
                None => {
                    if self.cascade_defaults && section != self.default_section {
                        return self.getuint(&self.default_section, &key);
                    }
                    Ok(None)
                }
            },
            None => {
                if self.cascade_defaults && section != self.default_section {
                    return self.getuint(&self.default_section, &key);
                }
                Ok(None)
            }
        }
    }

    ///Parses the stored value from the key stored in the defined section to a `f64`.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.load("tests/test.ini");
    ///let value = config.getfloat("values", "float").unwrap().unwrap();
    ///assert_eq!(value, 3.1415);  // value accessible!
    ///```
    ///Returns `Ok(Some(value))` of type `f64` if value is found or else returns `Ok(None)`.
    ///If the parsing fails, it returns an `Err(string)`.
    pub fn getfloat(&self, section: &str, key: &str) -> Result<Option<f64>, String> {
        let (section, key) = self.autocase(section, key);
        match self.map.get(&section) {
            Some(secmap) => match secmap.get(&key) {
                Some(val) => match val {
                    Some(inner) => match inner.parse::<f64>() {
                        Err(why) => Err(why.to_string()),
                        Ok(float) => Ok(Some(float)),
                    },
                    None => {
                        if self.cascade_defaults && section != self.default_section {
                            return self.getfloat(&self.default_section, &key);
                        }
                        Ok(None)
                    }
                },
                None => {
                    if self.cascade_defaults && section != self.default_section {
                        return self.getfloat(&self.default_section, &key);
                    }
                    Ok(None)
                }
            },
            None => {
                if self.cascade_defaults && section != self.default_section {
                    return self.getfloat(&self.default_section, &key);
                }
                Ok(None)
            }
        }
    }

    ///Returns a clone of the `Map` stored in our struct.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.read(String::from(
    ///  "[section]
    ///  key=values"));
    ///let map = config.get_map().unwrap();
    ///assert_eq!(map, *config.get_map_ref());  // the cloned map is basically a snapshot that you own
    ///```
    ///Returns `Some(map)` if map is non-empty or else returns `None`.
    ///Similar to `load()` but returns an `Option` type with the currently stored `Map`.
    pub fn get_map(&self) -> Option<Map<String, Map<String, Option<String>>>> {
        if self.map.is_empty() {
            None
        } else {
            Some(self.map.clone())
        }
    }

    ///Returns an immutable reference to the `Map` stored in our struct.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///let mapclone = config.read(String::from
    ///  ("[topsecrets]
    ///  Valueless key")).unwrap();
    /////Think of the clone as being a snapshot at a point of time while the reference always points to the current configuration.
    ///assert_eq!(*config.get_map_ref(), mapclone);  // same as expected.
    ///```
    ///If you just need to definitely mutate the map, use `get_mut_map()` instead. Alternatively, you can generate a snapshot by getting a clone
    ///with `get_map()` and work with that.
    pub fn get_map_ref(&self) -> &Map<String, Map<String, Option<String>>> {
        &self.map
    }

    ///Returns a mutable reference to the `Map` stored in our struct.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.read(String::from
    ///  ("[topsecrets]
    ///  Valueless key"));
    /////We can then get the mutable map and insert a value like:
    ///config.get_mut_map().get_mut("topsecrets").unwrap().insert(String::from("nuclear launch codes"), None);
    ///assert_eq!(config.get("topsecrets", "nuclear launch codes"), None);  // inserted successfully!
    ///```
    ///If you just need to access the map without mutating, use `get_map_ref()` or make a clone with `get_map()` instead.
    pub fn get_mut_map(&mut self) -> &mut Map<String, Map<String, Option<String>>> {
        &mut self.map
    }

    ///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.
    ///An existing value in the map  will be overwritten. You can also set `None` safely.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.read(String::from(
    ///  "[section]
    ///  key=value"));
    ///let key_value = String::from("value");
    ///config.set("section", "key", Some(key_value));
    ///config.set("section", "key", None);  // also valid!
    ///assert_eq!(config.get("section", "key"), None);  // correct!
    ///```
    ///Returns `None` if there is no existing value, else returns `Some(Option<String>)`, with the existing value being the wrapped `Option<String>`.
    ///If you want to insert using a string literal, use `setstr()` instead.
    pub fn set(
        &mut self,
        section: &str,
        key: &str,
        value: Option<String>,
    ) -> Option<Option<String>> {
        let (section, key) = self.autocase(section, key);
        match self.map.get_mut(&section) {
            Some(secmap) => secmap.insert(key, value),
            None => {
                let mut valmap: Map<String, Option<String>> = Map::new();
                valmap.insert(key, value);
                self.map.insert(section, valmap);
                None
            }
        }
    }

    ///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.
    ///An existing value in the map  will be overwritten. You can also set `None` safely.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.read(String::from(
    ///  "[section]
    ///  key=notvalue"));
    ///config.setstr("section", "key", Some("value"));
    ///config.setstr("section", "key", None);  // also valid!
    ///assert_eq!(config.get("section", "key"), None);  // correct!
    ///```
    ///Returns `None` if there is no existing value, else returns `Some(Option<String>)`, with the existing value being the wrapped `Option<String>`.
    ///If you want to insert using a `String`, use `set()` instead.
    pub fn setstr(
        &mut self,
        section: &str,
        key: &str,
        value: Option<&str>,
    ) -> Option<Option<String>> {
        let (section, key) = self.autocase(section, key);
        self.set(&section, &key, value.map(String::from))
    }

    ///Clears the map, removing all sections and properties from the hashmap. It keeps the allocated memory for reuse.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.read(String::from(
    ///  "[section]
    ///  key=somevalue"));
    ///config.clear();
    ///assert!(config.get_map_ref().is_empty());  // our map is empty!
    ///```
    ///Returns nothing.
    pub fn clear(&mut self) {
        self.map.clear();
    }

    ///Removes a section from the hashmap, returning the properties stored in the section if the section was previously in the map.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.read(String::from(
    ///  "[section]
    ///  updog=whatsupdog"));
    ///config.remove_section("section");  // this will return a cloned hashmap of the stored property
    ///assert!(config.get_map_ref().is_empty());  // with the last section removed, our map is now empty!
    ///```
    ///Returns `Some(section_map)` if the section exists or else, `None`.
    pub fn remove_section(&mut self, section: &str) -> Option<Map<String, Option<String>>> {
        let section = if self.case_sensitive {
            section.to_owned()
        } else {
            section.to_lowercase()
        };
        #[cfg(not(feature = "indexmap"))]
        {
            self.map.remove(&section)
        }
        #[cfg(feature = "indexmap")]
        {
            self.map.swap_remove(&section)
        }
    }

    ///Removes a key from a section in the hashmap, returning the value attached to the key if it was previously in the map.
    ///## Example
    ///```rust
    ///use configparser::ini::Ini;
    ///
    ///let mut config = Ini::new();
    ///config.read(String::from(
    ///  "[section]
    ///  updog=whatsupdog
    ///  [anothersection]
    ///  updog=differentdog"));
    ///let val = config.remove_key("anothersection", "updog").unwrap().unwrap();
    ///assert_eq!(val, String::from("differentdog"));  // with the last section removed, our map is now empty!
    ///```
    ///Returns `Some(Option<String>)` if the value exists or else, `None`.
    pub fn remove_key(&mut self, section: &str, key: &str) -> Option<Option<String>> {
        let (section, key) = self.autocase(section, key);
        #[cfg(not(feature = "indexmap"))]
        {
            self.map.get_mut(&section)?.remove(&key)
        }
        #[cfg(feature = "indexmap")]
        {
            self.map.get_mut(&section)?.swap_remove(&key)
        }
    }
}

#[cfg(feature = "tokio")]
impl Ini {
    ///Loads a file asynchronously from a defined path, parses it and puts the hashmap into our struct.
    ///At one time, it only stores one configuration, so each call to `load()` or `read()` will clear the existing `Map`, if present.
    ///
    ///Usage is similar to `load`, but `.await` must be called after along with the usual async rules.
    ///
    ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`.
    ///Use `get_mut_map()` if you want a mutable reference.
    pub async fn load_async<T: AsRef<Path>>(
        &mut self,
        path: T,
    ) -> Result<Map<String, Map<String, Option<String>>>, String> {
        self.map = match self.parse(match async_fs::read_to_string(&path).await {
            Err(why) => {
                return Err(format!(
                    "couldn't read {}: {}",
                    &path.as_ref().display(),
                    why
                ));
            }
            Ok(s) => s,
        }) {
            Err(why) => {
                return Err(format!(
                    "couldn't read {}: {}",
                    &path.as_ref().display(),
                    why
                ));
            }
            Ok(map) => map,
        };
        Ok(self.map.clone())
    }

    ///Loads a file from a defined path, parses it and applies it to the existing hashmap in our struct.
    ///While `load_async()` will clear the existing `Map`, `load_and_append_async()` applies the new values on top
    ///of the existing hashmap, preserving previous values.
    ///
    ///Usage is similar to `load_and_append`, but `.await` must be called after along with the usual async rules.
    ///
    ///Returns `Ok(map)` with a clone of the stored `Map` if no errors are thrown or else `Err(error_string)`.
    ///Use `get_mut_map()` if you want a mutable reference.
    pub async fn load_and_append_async<T: AsRef<Path>>(
        &mut self,
        path: T,
    ) -> Result<Map<String, Map<String, Option<String>>>, String> {
        let loaded = match self.parse(match async_fs::read_to_string(&path).await {
            Err(why) => {
                return Err(format!(
                    "couldn't read {}: {}",
                    &path.as_ref().display(),
                    why
                ));
            }
            Ok(s) => s,
        }) {
            Err(why) => {
                return Err(format!(
                    "couldn't read {}: {}",
                    &path.as_ref().display(),
                    why
                ));
            }
            Ok(map) => map,
        };

        for (section, section_map) in loaded.iter() {
            self.map
                .entry(section.clone())
                .or_insert_with(Map::new)
                .extend(section_map.clone());
        }

        Ok(self.map.clone())
    }

    ///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
    ///exists, it is truncated and the configuration is written to it.
    ///
    ///Usage is the same as `write`, but `.await` must be called after along with the usual async rules.
    ///
    ///Returns a `std::io::Result<()>` type dependent on whether the write was successful or not.
    pub async fn write_async<T: AsRef<Path>>(&self, path: T) -> std::io::Result<()> {
        async_fs::write(path.as_ref(), self.unparse(&WriteOptions::default())).await
    }

    ///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
    ///exists, it is truncated and the configuration is written to it.
    ///
    ///Usage is the same as `pretty_pretty_write`, but `.await` must be called after along with the usual async rules.
    ///
    ///Returns a `std::io::Result<()>` type dependent on whether the write was successful or not.
    pub async fn pretty_write_async<T: AsRef<Path>>(
        &self,
        path: T,
        write_options: &WriteOptions,
    ) -> std::io::Result<()> {
        async_fs::write(path.as_ref(), self.unparse(write_options)).await
    }
}
