ini_lib/
lib.rs

1use std::collections::HashMap;
2use std::fs::File;
3use std::io::Read;
4
5#[derive(Debug, Clone)]
6pub struct INI;
7
8#[derive(Debug, Clone)]
9pub struct Section {
10    pub name: String,
11    pub sub: HashMap<String, Vec<String>>,
12}
13
14
15
16/// ```
17/// use ini_lib::ini_str;
18/// let a = "[Interface]
19/// Address = 10.1.1.2/24
20/// PrivateKey = keykeykeykey
21/// ListenPort = 51820
22/// DNS = 8.8.8.8
23///
24/// [Peer]
25/// PublicKey = keykeykeykeykeykeykeykey
26/// Endpoint = 1.1.1.1:51820
27/// AllowedIPs = 10.1.1.5/32
28///
29/// [Peer]
30/// PublicKey = keykeykeykeykeykeykeykeykeykeykeykey
31/// PresharedKey = keykeykeykeykeykeykeykeykeykeykeykeykeykeykeykey
32/// Endpoint = 2.2.2.2:51820
33/// AllowedIPs = 10.13.13.0/24
34/// PersistentKeepalive = 25";
35///
36/// println!("{:#?}",ini_str!(a));
37/// ```
38#[macro_export]
39macro_rules! ini_str {
40    {$($data: expr),+} => {{
41		($($crate::from_str($data)),+)
42	}};
43}
44
45///```
46/// use ini_lib::ini_file;
47/// println!("{:#?}",ini_file!("test.ini"));
48/// ```
49#[macro_export]
50macro_rules! ini_file {
51    {$($data: expr),+} => {{
52		($($crate::from_file($data)),+)
53	}};
54}
55
56impl Default for Section {
57    fn default() -> Self {
58        Self {
59            name: String::new(),
60            sub: HashMap::new(),
61        }
62    }
63}
64
65impl Section {
66    pub fn is_empty(&self) -> bool {
67        self.name.is_empty()
68    }
69    pub fn clear(&mut self) {
70        self.name = String::new();
71        self.sub.clear();
72    }
73}
74
75
76pub fn from_str(s: &str) -> Result<Vec<Section>, String> {
77    let data = s
78        .trim()
79        .split("\n")
80        .filter(|x| {
81            let xx = x.trim();
82            !(xx.starts_with(";") || xx.starts_with("#"))
83        })
84        .map(|x| {
85            x.trim()
86                .split("=")
87                .map(|x| x.trim())
88                .filter(|x| {
89                    !x.to_string().eq("")
90                })
91                .collect::<Vec<_>>()
92        })
93        .filter(|x| !x.is_empty())
94        .collect::<Vec<_>>();
95
96    let mut tmp_section = Section::default();
97    let mut tmp_map: HashMap<String, Vec<String>> = HashMap::new();
98    let mut amaps = Vec::new();
99    for x in data {
100        if x[0].starts_with("[") && x[0].ends_with("]") {
101            if !tmp_section.is_empty() {
102                if !tmp_map.is_empty() {
103                    tmp_section.sub = tmp_map.clone();
104                    tmp_map.clear();
105                }
106                amaps.push(tmp_section.clone());
107                tmp_section.clear();
108            }
109            tmp_section.name = x[0].trim_start_matches('[').trim_end_matches(']').to_string();
110            continue;
111        }
112        if x.len() < 2 {
113            tmp_map.insert(x[0].to_string(), Vec::new());
114            continue;
115        }
116
117        tmp_map.entry(x[0].to_string()).or_insert_with(Vec::new).push(x[1].to_string());
118    }
119    if !tmp_section.is_empty() {
120        if !tmp_map.is_empty() {
121            tmp_section.sub = tmp_map.clone();
122        }
123        amaps.push(tmp_section.clone());
124    }
125    Ok(amaps)
126}
127
128pub fn from_file(path: &str) -> Result<Vec<Section>, String> {
129    return match File::open(path) {
130        Ok(mut s) => {
131            let mut buf = String::new();
132            return match s.read_to_string(&mut buf) {
133                Ok(_) => { from_str(buf.as_str()) }
134                Err(err) => { Err(err.to_string()) }
135            };
136        }
137        Err(err) => { Err(err.to_string()) }
138    };
139}
140
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn it_works() {
148        let a = "[Interface]
149Address = 10.1.1.2/24
150PrivateKey = keykeykeykey
151ListenPort = 51820
152DNS = 8.8.8.8
153
154[Peer]
155PublicKey = keykeykeykeykeykeykeykey
156Endpoint = 1.1.1.1:51820
157AllowedIPs = 10.1.1.5/32
158AllowedIPs = 10.1.1.2/32
159
160[Peer]
161PublicKey = keykeykeykeykeykeykeykeykeykeykeykey
162PresharedKey = keykeykeykeykeykeykeykeykeykeykeykeykeykeykeykey
163Endpoint = 2.2.2.2:51820
164AllowedIPs = 10.13.13.0/24
165PersistentKeepalive = 25";
166
167        println!("{:#?}", ini_str!(a));
168    }
169}