logo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use std::{collections::HashMap, fmt};

type ConfigSection = HashMap<String, String>;

/// An INI-like config file parser.
///  *
/// ```ini
/// ; This is a comment
/// foo = some value
/// [my section]
/// password = "  quotes are optional, and useful if you want to preserve surrounding spaces  "
/// ```
pub struct Config {
    pub main_section: ConfigSection,
    pub sections: HashMap<String, ConfigSection>,
}

// to Serialize back to an INI file. use fmt::Display
impl Config {
    pub fn new() -> Self {
        Self {
            main_section: ConfigSection::new(),
            sections: HashMap::new(),
        }
    }

    /// Parse the contents of an INI file.
    // static
    pub fn parse(text: String) -> Self {
        // let config = Config::new();

        // let commentPattern = ~/^\s*;/;
        // let sectionPattern = ~/^\s*\[\s*([^\]]*)\s*\]/;
        // let pair_pattern = ~/^\s*([\w\.\-_]+)\s*=\s*(.*)/;

        // let currentSection = config.mainSection;
        // for line in ~/\r\n|\r|\n/g.split(text) {
        //     if (commentPattern.match(line)) {
        //         // Ignore this line

        //     } else if (sectionPattern.match(line)) {
        //         let name = sectionPattern.matched(1);
        //         if (config.sections.contains_key(&name)) {
        //             // Handle duplicate sections, should this be allowed?
        //             currentSection = config.sections.get(name);
        //         } else {
        //             currentSection = ConfigSection::new();
        //             config.sections.set(name, currentSection);
        //         }

        //     } else if (pair_pattern.match(line)) {
        //         let key = pair_pattern.matched(1);
        //         let value = pair_pattern.matched(2);
        //         let quote = value.chars.nth(0).unwrap();
        //         if ((quote == '"'.to_digit(10) || quote == '\''.to_digit(10)) &&
        //                 value.chars.last() == quote) {
        //             // Trim off quotes
        //             value = value[1..value.len()-1];
        //         }
        //         currentSection.set(key, value
        //             // Unescape certain characters
        //             .replace("\\n", "\n")
        //             .replace("\\r", "\r")
        //             .replace("\\t", "\t")
        //             .replace("\\'", "\'")
        //             .replace("\\\"", "\"")
        //             .replace("\\\\", "\\")
        //         );
        //     }
        // }

        // return config;
        unimplemented!()
    }

    /// Shorthand for sections.get(name).
    #[inline]
    pub fn section(&self, name: String) -> Option<&ConfigSection> {
        self.sections.get(&name)
    }

    /// Searches for a value with a full path. A path is a section and key name separated by a dot. A
    /// path without a dot is assumed to be in the main section.
    ///  *
    /// Eg: get("foo.bar") is the same as section("foo").get("bar");
    pub fn get(&self, path: &String) -> Option<&String> {
        match path.find(".") {
            Some(idx) => {
                if let Some(section) = self.sections.get(&path[..idx]) {
                    // section.get(&path[idx + 1..].to_string())
                    todo!("should deal with it");
                } else {
                    None
                }
            }
            None => self.main_section.get(path),
        }
    }
}

impl fmt::Display for Config {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut out = String::new();
        for (key, value) in self.main_section.iter() {
            out.push_str(format!("{} = \"{}\"\n", key, value).as_str());
        }

        for name in self.sections.keys() {
            out.push_str(format!("[{}]\n", name).as_str());

            if let Some(section) = self.sections.get(name) {
                for (key, value) in section.iter() {
                    out.push_str(format!("{} =\"{}\"\n", key, value).as_str());
                }
            }
        }

        write!(f, "{}", out)
    }
}