1 use configparser::ini::{Ini, WriteOptions};
2 use std::error::Error;
3 
4 #[test]
5 #[allow(clippy::approx_constant)]
non_cs() -> Result<(), Box<dyn Error>>6 fn non_cs() -> Result<(), Box<dyn Error>> {
7     let mut config = Ini::new();
8     let map = config.load("tests/test.ini")?;
9     config.set_comment_symbols(&[';', '#', '!']);
10     let inpstring = config.read(
11         "defaultvalues=defaultvalues
12 		[topsecret]
13 		KFC = the secret herb is orega-
14                 colon:value after colon
15 		Empty string =
16 		None string
17         Password=[in-brackets]
18 		[ spacing ]
19 			indented=indented
20 		not indented = not indented				;testcomment
21 		!modified comment
22 		[values]#another comment
23 		Bool = True
24 		Boolcoerce = 0
25 		Int = -31415
26 		Uint = 31415
27 		Float = 3.1415"
28             .to_owned(),
29     )?;
30     assert_eq!(map, inpstring);
31     config.set("DEFAULT", "defaultvalues", Some("notdefault".to_owned()));
32     assert_eq!(
33         config.get("DEFAULT", "defaultvalues").unwrap(),
34         "notdefault"
35     );
36     config.setstr("DEFAULT", "defaultvalues", Some("defaultvalues"));
37     assert_eq!(
38         config.get("DEFAULT", "defaultvalues").unwrap(),
39         "defaultvalues"
40     );
41     config.setstr("DEFAULT", "defaultvalues", None);
42     config.write("output.ini")?;
43     let map2 = config.clone().load("output.ini")?;
44     assert_eq!(map2, *config.get_map_ref());
45     let map3 = config.clone().read(config.writes())?;
46     assert_eq!(map2, map3);
47     assert_eq!(config.sections().len(), 4);
48     assert_eq!(config.get("DEFAULT", "defaultvalues"), None);
49     assert_eq!(
50         config.get("topsecret", "KFC").unwrap(),
51         "the secret herb is orega-"
52     );
53     assert_eq!(config.get("topsecret", "Empty string").unwrap(), "");
54     assert_eq!(config.get("topsecret", "None string"), None);
55     assert_eq!(config.get("spacing", "indented").unwrap(), "indented");
56     assert_eq!(
57         config.get("spacing", "not indented").unwrap(),
58         "not indented"
59     );
60     assert_eq!(
61         config.get("topsecret", "colon").unwrap(),
62         "value after colon"
63     );
64     assert!(config.getbool("values", "Bool")?.unwrap());
65     assert!(!config.getboolcoerce("values", "Boolcoerce")?.unwrap());
66     assert_eq!(config.getint("values", "Int")?.unwrap(), -31415);
67     assert_eq!(config.getuint("values", "Uint")?.unwrap(), 31415);
68     assert_eq!(config.getfloat("values", "Float")?.unwrap(), 3.1415);
69     assert_eq!(config.getfloat("topsecret", "None string"), Ok(None));
70     assert_eq!(
71         map["default"]["defaultvalues"].clone().unwrap(),
72         "defaultvalues"
73     );
74     assert_eq!(
75         map["topsecret"]["kfc"].clone().unwrap(),
76         "the secret herb is orega-"
77     );
78     assert_eq!(map["topsecret"]["empty string"].clone().unwrap(), "");
79     assert_eq!(map["topsecret"]["none string"], None);
80     assert_eq!(map["spacing"]["indented"].clone().unwrap(), "indented");
81     assert_eq!(
82         map["spacing"]["not indented"].clone().unwrap(),
83         "not indented"
84     );
85     let mut config2 = config.clone();
86     let val = config2.remove_key("default", "defaultvalues");
87     assert_eq!(val, Some(None));
88     assert_eq!(config2.get("default", "defaultvalues"), None);
89     config2.remove_section("default");
90     assert_eq!(config2.get("default", "nope"), None);
91     let mut_map = config.get_mut_map();
92     mut_map.get_mut("topsecret").unwrap().insert(
93         String::from("none string"),
94         Some(String::from("None string")),
95     );
96     assert_eq!(
97         mut_map["topsecret"]["none string"].clone().unwrap(),
98         "None string"
99     );
100     mut_map.clear();
101     config2.clear();
102     assert_eq!(config.get_map_ref(), config2.get_map_ref());
103 
104     config.load("tests/test.ini")?;
105     config.read_and_append("defaultvalues=somenewvalue".to_owned())?;
106     assert_eq!(
107         config.get("default", "defaultvalues").unwrap(),
108         "somenewvalue"
109     );
110     assert_eq!(
111         config.get("topsecret", "KFC").unwrap(),
112         "the secret herb is orega-"
113     );
114 
115     let mut config3 = config.clone();
116     let mut_map = config3.get_mut_map();
117     mut_map.clear();
118     config3.load("tests/test.ini")?;
119     config3.load_and_append("tests/test_more.ini")?;
120     assert_eq!(
121         config3.get("default", "defaultvalues").unwrap(),
122         "overwritten"
123     );
124     assert_eq!(config3.get("topsecret", "KFC").unwrap(), "redacted");
125     // spacing -> indented exists in tests/test.ini, but not tests/test_more.ini
126     assert_eq!(config3.get("spacing", "indented").unwrap(), "indented");
127     assert!(!config3.getbool("values", "Bool")?.unwrap());
128 
129     Ok(())
130 }
131 
132 #[test]
133 #[allow(clippy::approx_constant)]
cs() -> Result<(), Box<dyn Error>>134 fn cs() -> Result<(), Box<dyn Error>> {
135     let mut config = Ini::new_cs();
136     let map = config.load("tests/test.ini")?;
137     config.set_comment_symbols(&[';', '#', '!']);
138     let inpstring = config.read(
139         "defaultvalues=defaultvalues
140         [topsecret]
141         KFC = the secret herb is orega-
142                 colon:value after colon
143         Empty string =
144         None string
145         Password=[in-brackets]
146         [ spacing ]
147             indented=indented
148         not indented = not indented             ;testcomment
149         !modified comment
150         [values]#another comment
151         Bool = True
152         Boolcoerce = 0
153         Int = -31415
154         Uint = 31415
155         Float = 3.1415"
156             .to_owned(),
157     )?;
158     assert_eq!(map, inpstring);
159     config.set("default", "defaultvalues", Some("notdefault".to_owned()));
160     assert_eq!(
161         config.get("default", "defaultvalues").unwrap(),
162         "notdefault"
163     );
164     config.setstr("default", "defaultvalues", Some("defaultvalues"));
165     assert_eq!(
166         config.get("default", "defaultvalues").unwrap(),
167         "defaultvalues"
168     );
169     config.setstr("default", "defaultvalues", None);
170     config.write("output2.ini")?;
171     let map2 = config.clone().load("output2.ini")?;
172     assert_eq!(map2, *config.get_map_ref());
173     let map3 = config.clone().read(config.writes())?;
174     assert_eq!(map2, map3);
175     assert_eq!(config.sections().len(), 4);
176     assert_eq!(config.get("default", "defaultvalues"), None);
177     assert_eq!(
178         config.get("topsecret", "KFC").unwrap(),
179         "the secret herb is orega-"
180     );
181     assert_eq!(config.get("topsecret", "Empty string").unwrap(), "");
182     assert_eq!(config.get("topsecret", "None string"), None);
183     assert_eq!(config.get("spacing", "indented").unwrap(), "indented");
184     assert_eq!(
185         config.get("spacing", "not indented").unwrap(),
186         "not indented"
187     );
188     assert_eq!(
189         config.get("topsecret", "colon").unwrap(),
190         "value after colon"
191     );
192     assert!(config.getbool("values", "Bool")?.unwrap());
193     assert!(!config.getboolcoerce("values", "Boolcoerce")?.unwrap());
194     assert_eq!(config.getint("values", "Int")?.unwrap(), -31415);
195     assert_eq!(config.getuint("values", "Uint")?.unwrap(), 31415);
196     assert_eq!(config.getfloat("values", "Float")?.unwrap(), 3.1415);
197     assert_eq!(config.getfloat("topsecret", "None string"), Ok(None));
198     assert_eq!(
199         map["default"]["defaultvalues"].clone().unwrap(),
200         "defaultvalues"
201     );
202     assert_eq!(
203         map["topsecret"]["KFC"].clone().unwrap(),
204         "the secret herb is orega-"
205     );
206     assert_eq!(map["topsecret"]["Empty string"].clone().unwrap(), "");
207     assert_eq!(map["topsecret"]["None string"], None);
208     assert_eq!(map["spacing"]["indented"].clone().unwrap(), "indented");
209     assert_eq!(
210         map["spacing"]["not indented"].clone().unwrap(),
211         "not indented"
212     );
213     let mut config2 = config.clone();
214     let val = config2.remove_key("default", "defaultvalues");
215     assert_eq!(val, Some(None));
216     assert_eq!(config2.get("default", "defaultvalues"), None);
217     config2.remove_section("default");
218     assert_eq!(config2.get("default", "nope"), None);
219     let mut_map = config.get_mut_map();
220     mut_map.get_mut("topsecret").unwrap().insert(
221         String::from("none string"),
222         Some(String::from("None string")),
223     );
224     assert_eq!(
225         mut_map["topsecret"]["none string"].clone().unwrap(),
226         "None string"
227     );
228     mut_map.clear();
229     config2.clear();
230     assert_eq!(config.get_map_ref(), config2.get_map_ref());
231     Ok(())
232 }
233 
234 #[test]
235 #[cfg(feature = "indexmap")]
sort_on_write() -> Result<(), Box<dyn Error>>236 fn sort_on_write() -> Result<(), Box<dyn Error>> {
237     let mut config = Ini::new_cs();
238     config.load("tests/test.ini")?;
239 
240     assert_eq!(
241         config.writes(),
242         "defaultvalues=defaultvalues
243 [topsecret]
244 KFC=the secret herb is orega-
245 colon=value after colon
246 Empty string=
247 None string
248 Password=[in-brackets]
249 [spacing]
250 indented=indented
251 not indented=not indented
252 [values]
253 Bool=True
254 Boolcoerce=0
255 Int=-31415
256 Uint=31415
257 Float=3.1415
258 "
259     );
260 
261     Ok(())
262 }
263 
264 #[test]
265 #[cfg(feature = "indexmap")]
pretty_writes_result_is_formatted_correctly() -> Result<(), Box<dyn Error>>266 fn pretty_writes_result_is_formatted_correctly() -> Result<(), Box<dyn Error>> {
267     use configparser::ini::IniDefault;
268 
269     const OUT_FILE_CONTENTS: &str = "defaultvalues=defaultvalues
270 [topsecret]
271 KFC=the secret herb is orega-
272 Empty string=
273 None string
274 Password=[in-brackets]
275 [Section]
276 Key1: Value1
277 Key2: this is a haiku
278     spread across separate lines
279     a single value
280 Key3: another value
281 ";
282 
283     let mut ini_defaults = IniDefault::default();
284     ini_defaults.case_sensitive = true;
285     ini_defaults.multiline = true;
286     let mut config = Ini::new_from_defaults(ini_defaults);
287     config.read(OUT_FILE_CONTENTS.to_owned())?;
288 
289     let mut write_options = WriteOptions::default();
290     write_options.space_around_delimiters = true;
291     write_options.multiline_line_indentation = 2;
292     write_options.blank_lines_between_sections = 1;
293     assert_eq!(
294         config.pretty_writes(&write_options),
295         "defaultvalues = defaultvalues
296 
297 [topsecret]
298 KFC = the secret herb is orega-
299 Empty string =
300 None string
301 Password = [in-brackets]
302 
303 [Section]
304 Key1 = Value1
305 Key2 = this is a haiku
306   spread across separate lines
307   a single value
308 Key3 = another value
309 "
310     );
311 
312     Ok(())
313 }
314 
315 #[test]
316 #[cfg(feature = "indexmap")]
317 #[cfg(feature = "async-std")]
318 #[cfg(feature = "tokio")]
pretty_write_result_is_formatted_correctly() -> Result<(), Box<dyn Error>>319 fn pretty_write_result_is_formatted_correctly() -> Result<(), Box<dyn Error>> {
320     use configparser::ini::IniDefault;
321 
322     const OUT_FILE_CONTENTS: &str = "defaultvalues=defaultvalues
323 [topsecret]
324 KFC=the secret herb is orega-
325 Empty string=
326 None string
327 Password=[in-brackets]
328 [Section]
329 Key1: Value1
330 Key2: this is a haiku
331     spread across separate lines
332     a single value
333 Key3: another value
334 ";
335 
336     let mut ini_defaults = IniDefault::default();
337     ini_defaults.case_sensitive = true;
338     ini_defaults.multiline = true;
339     let mut config = Ini::new_from_defaults(ini_defaults);
340     config.read(OUT_FILE_CONTENTS.to_owned())?;
341 
342     let mut write_options = WriteOptions::default();
343     write_options.space_around_delimiters = true;
344     write_options.multiline_line_indentation = 2;
345     write_options.blank_lines_between_sections = 1;
346     config.pretty_write("pretty_output.ini", &write_options)?;
347 
348     let file_contents = std::fs::read_to_string("pretty_output.ini")?;
349     assert_eq!(
350         file_contents,
351         "defaultvalues = defaultvalues
352 
353 [topsecret]
354 KFC = the secret herb is orega-
355 Empty string =
356 None string
357 Password = [in-brackets]
358 
359 [Section]
360 Key1 = Value1
361 Key2 = this is a haiku
362   spread across separate lines
363   a single value
364 Key3 = another value
365 "
366     );
367 
368     Ok(())
369 }
370 
371 #[tokio::test]
372 #[cfg(feature = "indexmap")]
373 #[cfg(feature = "async-std")]
374 #[cfg(feature = "tokio")]
async_pretty_print_result_is_formatted_correctly() -> Result<(), Box<dyn Error>>375 async fn async_pretty_print_result_is_formatted_correctly() -> Result<(), Box<dyn Error>> {
376     use configparser::ini::IniDefault;
377 
378     const OUT_FILE_CONTENTS: &str = "defaultvalues=defaultvalues
379 [topsecret]
380 KFC=the secret herb is orega-
381 Empty string=
382 None string
383 Password=[in-brackets]
384 [Section]
385 Key1: Value1
386 Key2: this is a haiku
387     spread across separate lines
388     a single value
389 Key3: another value
390 ";
391 
392     let mut ini_defaults = IniDefault::default();
393     ini_defaults.case_sensitive = true;
394     ini_defaults.multiline = true;
395     let mut config = Ini::new_from_defaults(ini_defaults);
396     config.read(OUT_FILE_CONTENTS.to_owned())?;
397 
398     let mut write_options = WriteOptions::default();
399     write_options.space_around_delimiters = true;
400     write_options.multiline_line_indentation = 2;
401     write_options.blank_lines_between_sections = 1;
402     config
403         .pretty_write_async("pretty_output_async.ini", &write_options)
404         .await
405         .map_err(|e| e.to_string())?;
406 
407     let file_contents = std::fs::read_to_string("pretty_output_async.ini")?;
408     assert_eq!(
409         file_contents,
410         "defaultvalues = defaultvalues
411 
412 [topsecret]
413 KFC = the secret herb is orega-
414 Empty string =
415 None string
416 Password = [in-brackets]
417 
418 [Section]
419 Key1 = Value1
420 Key2 = this is a haiku
421   spread across separate lines
422   a single value
423 Key3 = another value
424 "
425     );
426 
427     Ok(())
428 }
429 
430 #[tokio::test]
431 #[cfg(feature = "async-std")]
432 #[cfg(feature = "tokio")]
async_load_write() -> Result<(), Box<dyn Error>>433 async fn async_load_write() -> Result<(), Box<dyn Error>> {
434     const OUT_FILE_CONTENTS: &str = "defaultvalues=defaultvalues
435     [topsecret]
436     KFC = the secret herb is orega-
437             colon:value after colon
438     Empty string =
439     None string
440     Password=[in-brackets]
441     [ spacing ]
442         indented=indented
443     not indented = not indented             ;testcomment
444     !modified comment
445     [values]#another comment
446     Bool = True
447     Boolcoerce = 0
448     Int = -31415
449     Uint = 31415
450     Float = 3.1415";
451 
452     let mut config = Ini::new();
453     config.read(OUT_FILE_CONTENTS.to_owned())?;
454     config.write("output_sync.ini")?;
455 
456     let mut config_async = Ini::new();
457     config_async.read(OUT_FILE_CONTENTS.to_owned())?;
458     config_async
459         .write_async("output_async.ini")
460         .await
461         .map_err(|e| e.to_string())?;
462 
463     let mut sync_content = Ini::new();
464     sync_content.load("output_sync.ini")?;
465 
466     let mut async_content = Ini::new();
467     async_content.load_async("output_async.ini").await?;
468 
469     assert_eq!(sync_content, async_content);
470 
471     Ok(())
472 }
473 
474 #[tokio::test]
475 #[cfg(feature = "async-std")]
476 #[cfg(feature = "tokio")]
async_load_and_append() -> Result<(), Box<dyn Error>>477 async fn async_load_and_append() -> Result<(), Box<dyn Error>> {
478     let mut sync_content = Ini::new();
479     sync_content.load("tests/test.ini")?;
480     sync_content.load_and_append("tests/test_more.ini")?;
481 
482     let mut async_content = Ini::new();
483     async_content.load_async("tests/test.ini").await?;
484     async_content
485         .load_and_append_async("tests/test_more.ini")
486         .await?;
487 
488     assert_eq!(sync_content, async_content);
489 
490     Ok(())
491 }
492 
493 #[test]
494 #[cfg(feature = "indexmap")]
multiline_off() -> Result<(), Box<dyn Error>>495 fn multiline_off() -> Result<(), Box<dyn Error>> {
496     let mut config = Ini::new_cs();
497     config.load("tests/test_multiline.ini")?;
498 
499     let map = config.get_map_ref();
500 
501     let section = map.get("Section").unwrap();
502 
503     assert_eq!(config.get("Section", "Key1").unwrap(), "Value1");
504     assert_eq!(config.get("Section", "Key2").unwrap(), "Value Two");
505     assert_eq!(config.get("Section", "Key3").unwrap(), "this is a haiku");
506     assert!(section.contains_key("spread across separate lines"));
507     assert!(section.contains_key("a single value"));
508 
509     assert_eq!(config.get("Section", "Key4").unwrap(), "Four");
510 
511     assert_eq!(
512         config.writes(),
513         "[Section]
514 Key1=Value1
515 Key2=Value Two
516 Key3=this is a haiku
517 spread across separate lines
518 a single value
519 Key4=Four
520 "
521     );
522 
523     Ok(())
524 }
525 
526 #[test]
527 #[cfg(feature = "indexmap")]
multiline_on() -> Result<(), Box<dyn Error>>528 fn multiline_on() -> Result<(), Box<dyn Error>> {
529     let mut config = Ini::new_cs();
530     config.set_multiline(true);
531     config.load("tests/test_multiline.ini")?;
532 
533     assert_eq!(config.get("Section", "Key1").unwrap(), "Value1");
534     assert_eq!(config.get("Section", "Key2").unwrap(), "Value Two");
535     assert_eq!(
536         config.get("Section", "Key3").unwrap(),
537         "this is a haiku\nspread across separate lines\na single value"
538     );
539     assert_eq!(config.get("Section", "Key4").unwrap(), "Four");
540 
541     assert_eq!(
542         config.writes(),
543         "[Section]
544 Key1=Value1
545 Key2=Value Two
546 Key3=this is a haiku
547     spread across separate lines
548     a single value
549 Key4=Four
550 "
551     );
552 
553     Ok(())
554 }
555