1 use nom::{
2 bytes::complete::{is_a, tag, take_till, take_while},
3 character::complete::{alphanumeric1 as alphanumeric, char, space0 as space},
4 combinator::opt,
5 multi::many0,
6 sequence::{delimited, pair, terminated, tuple},
7 IResult,
8 };
9
10 use std::collections::HashMap;
11
is_line_ending_or_comment(chr: char) -> bool12 fn is_line_ending_or_comment(chr: char) -> bool {
13 chr == ';' || chr == '\n'
14 }
15
not_line_ending(i: &str) -> IResult<&str, &str>16 fn not_line_ending(i: &str) -> IResult<&str, &str> {
17 take_while(|c| c != '\r' && c != '\n')(i)
18 }
19
space_or_line_ending(i: &str) -> IResult<&str, &str>20 fn space_or_line_ending(i: &str) -> IResult<&str, &str> {
21 is_a(" \r\n")(i)
22 }
23
category(i: &str) -> IResult<&str, &str>24 fn category(i: &str) -> IResult<&str, &str> {
25 terminated(
26 delimited(char('['), take_while(|c| c != ']'), char(']')),
27 opt(is_a(" \r\n")),
28 )(i)
29 }
30
key_value(i: &str) -> IResult<&str, (&str, &str)>31 fn key_value(i: &str) -> IResult<&str, (&str, &str)> {
32 let (i, key) = alphanumeric(i)?;
33 let (i, _) = tuple((opt(space), tag("="), opt(space)))(i)?;
34 let (i, val) = take_till(is_line_ending_or_comment)(i)?;
35 let (i, _) = opt(space)(i)?;
36 let (i, _) = opt(pair(tag(";"), not_line_ending))(i)?;
37 let (i, _) = opt(space_or_line_ending)(i)?;
38
39 Ok((i, (key, val)))
40 }
41
keys_and_values_aggregator(i: &str) -> IResult<&str, Vec<(&str, &str)>>42 fn keys_and_values_aggregator(i: &str) -> IResult<&str, Vec<(&str, &str)>> {
43 many0(key_value)(i)
44 }
45
keys_and_values(input: &str) -> IResult<&str, HashMap<&str, &str>>46 fn keys_and_values(input: &str) -> IResult<&str, HashMap<&str, &str>> {
47 match keys_and_values_aggregator(input) {
48 Ok((i, tuple_vec)) => Ok((i, tuple_vec.into_iter().collect())),
49 Err(e) => Err(e),
50 }
51 }
52
category_and_keys(i: &str) -> IResult<&str, (&str, HashMap<&str, &str>)>53 fn category_and_keys(i: &str) -> IResult<&str, (&str, HashMap<&str, &str>)> {
54 pair(category, keys_and_values)(i)
55 }
56
categories_aggregator(i: &str) -> IResult<&str, Vec<(&str, HashMap<&str, &str>)>>57 fn categories_aggregator(i: &str) -> IResult<&str, Vec<(&str, HashMap<&str, &str>)>> {
58 many0(category_and_keys)(i)
59 }
60
categories(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str>>>61 fn categories(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str>>> {
62 match categories_aggregator(input) {
63 Ok((i, tuple_vec)) => Ok((i, tuple_vec.into_iter().collect())),
64 Err(e) => Err(e),
65 }
66 }
67
68 #[test]
parse_category_test()69 fn parse_category_test() {
70 let ini_file = "[category]
71
72 parameter=value
73 key = value2";
74
75 let ini_without_category = "parameter=value
76 key = value2";
77
78 let res = category(ini_file);
79 println!("{:?}", res);
80 match res {
81 Ok((i, o)) => println!("i: {} | o: {:?}", i, o),
82 _ => println!("error"),
83 }
84
85 assert_eq!(res, Ok((ini_without_category, "category")));
86 }
87
88 #[test]
parse_key_value_test()89 fn parse_key_value_test() {
90 let ini_file = "parameter=value
91 key = value2";
92
93 let ini_without_key_value = "key = value2";
94
95 let res = key_value(ini_file);
96 println!("{:?}", res);
97 match res {
98 Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
99 _ => println!("error"),
100 }
101
102 assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
103 }
104
105 #[test]
parse_key_value_with_space_test()106 fn parse_key_value_with_space_test() {
107 let ini_file = "parameter = value
108 key = value2";
109
110 let ini_without_key_value = "key = value2";
111
112 let res = key_value(ini_file);
113 println!("{:?}", res);
114 match res {
115 Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
116 _ => println!("error"),
117 }
118
119 assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
120 }
121
122 #[test]
parse_key_value_with_comment_test()123 fn parse_key_value_with_comment_test() {
124 let ini_file = "parameter=value;abc
125 key = value2";
126
127 let ini_without_key_value = "key = value2";
128
129 let res = key_value(ini_file);
130 println!("{:?}", res);
131 match res {
132 Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
133 _ => println!("error"),
134 }
135
136 assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
137 }
138
139 #[test]
parse_multiple_keys_and_values_test()140 fn parse_multiple_keys_and_values_test() {
141 let ini_file = "parameter=value;abc
142
143 key = value2
144
145 [category]";
146
147 let ini_without_key_value = "[category]";
148
149 let res = keys_and_values(ini_file);
150 println!("{:?}", res);
151 match res {
152 Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o),
153 _ => println!("error"),
154 }
155
156 let mut expected: HashMap<&str, &str> = HashMap::new();
157 expected.insert("parameter", "value");
158 expected.insert("key", "value2");
159 assert_eq!(res, Ok((ini_without_key_value, expected)));
160 }
161
162 #[test]
parse_category_then_multiple_keys_and_values_test()163 fn parse_category_then_multiple_keys_and_values_test() {
164 //FIXME: there can be an empty line or a comment line after a category
165 let ini_file = "[abcd]
166 parameter=value;abc
167
168 key = value2
169
170 [category]";
171
172 let ini_after_parser = "[category]";
173
174 let res = category_and_keys(ini_file);
175 println!("{:?}", res);
176 match res {
177 Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o),
178 _ => println!("error"),
179 }
180
181 let mut expected_h: HashMap<&str, &str> = HashMap::new();
182 expected_h.insert("parameter", "value");
183 expected_h.insert("key", "value2");
184 assert_eq!(res, Ok((ini_after_parser, ("abcd", expected_h))));
185 }
186
187 #[test]
parse_multiple_categories_test()188 fn parse_multiple_categories_test() {
189 let ini_file = "[abcd]
190
191 parameter=value;abc
192
193 key = value2
194
195 [category]
196 parameter3=value3
197 key4 = value4
198 ";
199
200 let res = categories(ini_file);
201 //println!("{:?}", res);
202 match res {
203 Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o),
204 _ => println!("error"),
205 }
206
207 let mut expected_1: HashMap<&str, &str> = HashMap::new();
208 expected_1.insert("parameter", "value");
209 expected_1.insert("key", "value2");
210 let mut expected_2: HashMap<&str, &str> = HashMap::new();
211 expected_2.insert("parameter3", "value3");
212 expected_2.insert("key4", "value4");
213 let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
214 expected_h.insert("abcd", expected_1);
215 expected_h.insert("category", expected_2);
216 assert_eq!(res, Ok(("", expected_h)));
217 }
218