• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*!
2 This crate provides the `Ini` struct which implements a basic configuration language which provides a structure similar to what’s found in Windows' `ini` files.
3 You can use this to write Rust programs which can be customized by end users easily.
4 
5 This is a simple configuration parsing utility with no dependencies built on Rust. It is inspired by Python's `configparser`.
6 
7 The current release is stable and changes will take place at a slower pace. We'll be keeping semver in mind for future releases as well.
8 
9 ## �� Quick Start
10 
11 A basic `ini`-syntax file (we say ini-syntax files because the files don't need to be necessarily `*.ini`) looks like this:
12 ```INI
13 [DEFAULT]
14 key1 = value1
15 pizzatime = yes
16 cost = 9
17 
18 [topsecrets]
19 nuclear launch codes = topsecret
20 
21 [github.com]
22 User = QEDK
23 ```
24 Essentially, the syntax consists of sections, each of which can which contains keys with values. The `Ini` struct can read and write such values to
25 strings as well as files.
26 
27 ## ➕ Supported datatypes
28 `configparser` does not guess the datatype of values in configuration files and stores everything as strings. However, some datatypes are so common
29 that it's a safe bet that some values need to be parsed in other types. For this, the `Ini` struct provides easy functions like `getint()`, `getuint()`,
30 `getfloat()` and `getbool()`. The only bit of extra magic involved is that the `getbool()` function will treat boolean values case-insensitively (so
31 `true` is the same as `True` just like `TRUE`). The crate also provides a stronger `getboolcoerce()` function that parses more values (such as `T`, `yes` and `0`, all case-insensitively), the function's documentation will give you the exact details.
32 ```rust
33 use configparser::ini::Ini;
34 
35 let mut config = Ini::new();
36 config.read(String::from(
37   "[somesection]
38   someintvalue = 5"));
39 let my_value = config.getint("somesection", "someintvalue").unwrap().unwrap();
40 assert_eq!(my_value, 5); // value accessible!
41 
42 //You can ofcourse just choose to parse the values yourself:
43 let my_string = String::from("1984");
44 let my_int = my_string.parse::<i32>().unwrap();
45 ```
46 
47 
48 ## �� Supported `ini` file structure
49 A configuration file can consist of sections, each led by a `[section-name]` header, followed by key-value entries separated by a delimiter (`=` and `:`). By default, section names and key names are case-insensitive. Case-sensitivity can be enabled using the `Ini::new_cs()` constructor. All leading and trailing whitespace is removed from stored keys, values and section names.
50 Key values can be omitted, in which case the key-value delimiter
51 may also be left out (but this is different from putting a delimiter, we'll
52 explain it later). You can use comment symbols (`;` and `#` to denote comments). This can be configured with the `set_comment_symbols()` method in the
53 API. Keep in mind that key-value pairs or section headers cannot span multiple lines.
54 Owing to how ini files usually are, this means that `[`, `]`, `=`, `:`, `;` and `#` are special symbols by default (this crate will allow you to use `]` sparingly).
55 Let's take for example:
56 ```INI
57 [section headers are case-insensitive by default]
58 [   section headers are case-insensitive by default   ]
59 are the section headers above same? = yes
60 sectionheaders_and_keysarestored_in_lowercase? = yes
61 keys_are_also_case_insensitive = Values are case sensitive
62 Case-sensitive_keys_and_sections = using a special constructor
63 you can also use colons : instead of the equal symbol
64 ;anything after a comment symbol is ignored
65 #this is also a comment
66 spaces in keys=allowed ;and everything before this is still valid!
67 spaces in values=allowed as well
68 spaces around the delimiter = also OK
69 
70 
71 [All values are strings]
72 values like this= 0000
73 or this= 0.999
74 are they treated as numbers? = no
75 integers, floats and booleans are held as= strings
76 
77 [value-less?]
78 a_valueless_key_has_None
79 this key has an empty string value has Some("") =
80 
81     [indented sections]
82         can_values_be_as_well = True
83         purpose = formatting for readability
84         is_this_same     =        yes
85             is_this_same=yes
86 
87 ```
88 An important thing to note is that values with the same keys will get updated, this means that the last inserted key (whether that's a section header
89 or property key) is the one that remains in the `HashMap`.
90 The only bit of magic the API does is the section-less properties are put in a section called "default". You can configure this variable via the API.
91 Keep in mind that a section named "default" is also treated as sectionless so the output files remains consistent with no section header.
92 
93 ## Usage
94 Let's take another simple `ini` file and talk about working with it:
95 ```INI
96 [topsecret]
97 KFC = the secret herb is orega-
98 
99 [values]
100 Uint = 31415
101 ```
102 If you read the above sections carefully, you'll know that 1) all the keys are stored in lowercase, 2) `get()` can make access in a case-insensitive
103 manner and 3) we can use `getint()` to parse the `Int` value into an `i64`. Let's see that in action.
104 
105 ```rust
106 use configparser::ini::{Ini, WriteOptions};
107 use std::error::Error;
108 
109 fn main() -> Result<(), Box<dyn Error>> {
110   let mut config = Ini::new();
111 
112   // You can easily load a file to get a clone of the map:
113   let map = config.load("tests/test.ini")?;
114   println!("{:?}", map);
115   // You can also safely not store the reference and access it later with get_map_ref() or get a clone with get_map()
116 
117   // If you want to access the value, then you can simply do:
118   let val = config.get("TOPSECRET", "KFC").unwrap();
119   // Notice how get() can access indexes case-insensitively.
120 
121   assert_eq!(val, "the secret herb is orega-"); // value accessible!
122 
123   // What if you want remove KFC's secret recipe? Just use set():
124   config.set("topsecret", "kfc", None);
125 
126   assert_eq!(config.get("TOPSECRET", "KFC"), None); // as expected!
127 
128   // What if you want to get an unsigned integer?
129   let my_number = config.getuint("values", "Uint")?.unwrap();
130   assert_eq!(my_number, 31415); // and we got it!
131   // The Ini struct provides more getters for primitive datatypes.
132 
133   // You can also access it like a normal hashmap:
134   let innermap = map["topsecret"].clone();
135   // Remember that all indexes are stored in lowercase!
136 
137   // You can easily write the currently stored configuration to a file with the `write` method. This creates a compact format with as little spacing as possible:
138   config.write("output.ini");
139 
140   // You can write the currently stored configuration with different spacing to a file with the `pretty_write` method:
141   let write_options = WriteOptions::new_with_params(true, 2, 1);
142   // or you can use the default configuration as `WriteOptions::new()`
143   config.pretty_write("pretty_output.ini", &write_options);
144 
145   // If you want to simply mutate the stored hashmap, you can use get_mut_map()
146   let map = config.get_mut_map();
147   // You can then use normal HashMap functions on this map at your convenience.
148   // Remember that functions which rely on standard formatting might stop working
149   // if it's mutated differently.
150 
151   // If you want a case-sensitive map, just do:
152   let mut config = Ini::new_cs();
153   // This automatically changes the behaviour of every function and parses the file as case-sensitive.
154 
155   Ok(())
156 }
157 ```
158 */
159 pub mod ini;
160