• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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