1# configparser
2[![Build Status](https://github.com/QEDK/configparser-rs/actions/workflows/rust.yaml/badge.svg)](https://github.com/QEDK/configparser-rs/actions/workflows/rust.yaml) [![Crates.io](https://img.shields.io/crates/l/configparser?color=black)](LICENSE-MIT) [![Crates.io](https://img.shields.io/crates/v/configparser?color=black)](https://crates.io/crates/configparser) [![Released API docs](https://docs.rs/configparser/badge.svg)](https://docs.rs/configparser) [![Maintenance](https://img.shields.io/maintenance/yes/2024)](https://github.com/QEDK/configparser-rs)
3
4This crate provides the `Ini` struct which implements a basic configuration language which provides a structure similar to what’s found in Windows' `ini` files. You can use this to write Rust programs which can be customized by end users easily.
5
6This is a simple configuration parsing utility with no dependencies built on Rust. It is inspired by Python's `configparser`.
7
8The 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.
9
10## �� Quick Start
11
12A basic `ini`-syntax file (we say ini-syntax files because the files don't need to be necessarily `*.ini`) looks like this:
13```INI
14[DEFAULT]
15key1 = value1
16pizzatime = yes
17cost = 9
18
19[topsecrets]
20nuclear launch codes = topsecret
21
22[github.com]
23User = QEDK
24```
25Essentially, the syntax consists of sections, each of which can which contains keys with values. The `Ini` struct can read and write such values to
26strings as well as files.
27
28### �� Installation
29You can install this easily via `cargo` by including it in your `Cargo.toml` file like:
30```TOML
31[dependencies]
32configparser = "3.0.4"
33```
34
35## ➕ Supported datatypes
36`configparser` does not guess the datatype of values in configuration files and stores everything as strings. However, some datatypes are so common
37that 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()`,
38`getfloat()` and `getbool()`. The only bit of extra magic involved is that the `getbool()` function will treat boolean values case-insensitively (so
39`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.
40```rust
41use configparser::ini::Ini;
42
43let mut config = Ini::new();
44config.read(String::from(
45  "[somesection]
46  someintvalue = 5"));
47let my_value = config.getint("somesection", "someintvalue").unwrap().unwrap();
48assert_eq!(my_value, 5); // value accessible!
49
50//You can ofcourse just choose to parse the values yourself:
51let my_string = String::from("1984");
52let my_int = my_string.parse::<i32>().unwrap();
53```
54
55## �� Supported `ini` file structure
56A 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.
57Key values can be omitted, in which case the key-value delimiter
58may also be left out (but this is different from putting a delimiter, we'll
59explain it later). You can use comment symbols (`;` and `#` to denote comments). This can be configured with the `set_comment_symbols()` method in the
60API. Keep in mind that key-value pairs or section headers cannot span multiple lines.
61Owing to how ini files usually are, this means that `[`, `]`, `=`, `:`, `;` and `#` are special symbols by default (this crate will allow you to use `]` sparingly).
62
63Let's take for example:
64```INI
65[section headers are case-insensitive by default]
66[   section headers are case-insensitive by default   ]
67are the section headers above same? = yes
68sectionheaders_and_keysarestored_in_lowercase? = yes
69keys_are_also_case_insensitive = Values are case sensitive
70Case-sensitive_keys_and_sections = using a special constructor
71you can also use colons : instead of the equal symbol
72;anything after a comment symbol is ignored
73#this is also a comment
74spaces in keys=allowed ;and everything before this is still valid!
75spaces in values=allowed as well
76spaces around the delimiter = also OK
77
78
79[All values are strings]
80values like this= 0000
81or this= 0.999
82are they treated as numbers? = no
83integers, floats and booleans are held as= strings
84
85[value-less?]
86a_valueless_key_has_None
87this key has an empty string value has Some("") =
88
89    [indented sections]
90        can_values_be_as_well = True
91        purpose = formatting for readability
92        is_this_same     =        yes
93            is_this_same=yes
94
95```
96An 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
97or property key) is the one that remains in the `HashMap`.
98The 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.
99Keep in mind that a section named "default" is also treated as sectionless so the output files remains consistent with no section header.
100
101## �� Usage
102Let's take another simple `ini` file and talk about working with it:
103```INI
104[topsecret]
105KFC = the secret herb is orega-
106
107[values]
108Uint = 31415
109```
110If 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
111manner and 3) we can use `getuint()` to parse the `Uint` value into an `u64`. Let's see that in action.
112
113```rust
114use configparser::ini::{Ini, WriteOptions};
115use std::error::Error;
116
117fn main() -> Result<(), Box<dyn Error>> {
118  let mut config = Ini::new();
119
120  // You can easily load a file to get a clone of the map:
121  let map = config.load("tests/test.ini")?;
122  println!("{:?}", map);
123  // You can also safely not store the reference and access it later with get_map_ref() or get a clone with get_map()
124
125  // If you want to access the value, then you can simply do:
126  let val = config.get("TOPSECRET", "KFC").unwrap();
127  // Notice how get() can access indexes case-insensitively.
128
129  assert_eq!(val, "the secret herb is orega-"); // value accessible!
130
131  // What if you want remove KFC's secret recipe? Just use set():
132  config.set("topsecret", "kfc", None);
133
134  assert_eq!(config.get("TOPSECRET", "KFC"), None); // as expected!
135
136  // What if you want to get an unsigned integer?
137  let my_number = config.getuint("values", "Uint")?.unwrap();
138  assert_eq!(my_number, 31415); // and we got it!
139  // The Ini struct provides more getters for primitive datatypes.
140
141  // You can also access it like a normal hashmap:
142  let innermap = map["topsecret"].clone();
143  // Remember that all indexes are stored in lowercase!
144
145  // 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:
146  config.write("output.ini");
147
148  // You can write the currently stored configuration with different spacing to a file with the `pretty_write` method:
149  let write_options = WriteOptions::new_with_params(true, 2, 1);
150  // or you can use the default configuration as `WriteOptions::new()`
151  config.pretty_write("pretty_output.ini", &write_options);
152
153  // If you want to simply mutate the stored hashmap, you can use get_mut_map()
154  let map = config.get_mut_map();
155  // You can then use normal HashMap functions on this map at your convenience.
156  // Remember that functions which rely on standard formatting might stop working
157  // if it's mutated differently.
158
159  // If you want a case-sensitive map, just do:
160  let mut config = Ini::new_cs();
161  // This automatically changes the behaviour of every function and parses the file as case-sensitive.
162
163  Ok(())
164}
165```
166The `Ini` struct offers great support for type conversion and type setting safely, as well as map accesses. See the API for more verbose documentation.
167
168## ��Features
169
170 - *indexmap*: Activating the `indexmap` feature allows using [indexmap](https://crates.io/crates/indexmap) in place
171  of `HashMap` to store the sections and keys. This ensures that insertion order is preserved when iterating on or
172  serializing the Ini object.
173  Due to the nature of indexmap, it offers mostly similar performance to stdlib HashMaps but with
174  [slower lookup times](https://github.com/bluss/indexmap#performance).
175
176You can activate it by adding it as a feature like this:
177```TOML
178[dependencies]
179configparser = { version = "3.0.4", features = ["indexmap"] }
180```
181
182 - *tokio*: Activating the `tokio` feature adds asynchronous functions for reading from (`load_async()`) and
183   writing to (`write_async()`) files using [tokio](https://crates.io/crates/tokio).
184
185You can activate it by adding it as a feature like this:
186```TOML
187[dependencies]
188configparser = { version = "3.0.4", features = ["tokio"] }
189```
190
191## �� License
192
193Licensed under either of
194
195 * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
196 * Lesser General Public license v3.0 or later ([LICENSE-LGPL](LICENSE-LGPL) or https://www.gnu.org/licenses/lgpl-3.0.html)
197
198at your option.
199
200### ✏ Contribution
201
202Unless you explicitly state otherwise, any contribution intentionally submitted
203for inclusion in the work by you, as defined in the LGPL-3.0 license, shall be dual licensed as above, without any
204additional terms or conditions.
205
206## �� Changelog
207
208Old changelogs are in [CHANGELOG.md](CHANGELOG.md).
209- 3.0.0
210  - �� **BREAKING** `IniDefault` is now a non-exhaustive struct, this will make future upgrades easier and non-breaking in nature. This change might also have a few implications in updating your existing codebase, please read the [official docs](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute) for more guidance.
211  - `IniDefault` is now internally used for generating defaults, reducing crate size.
212  - �� There is now a new optional `indexmap` feature that preserves insertion order of your loaded configurations.
213- 3.0.1
214  - Uses `CRLF` line endings for Windows files.
215  - Bumps crate to 2021 edition.
216  - Adds features to CI pipeline.
217- 3.0.2
218  - Adds support for multi-line key-value pairs.
219  - Adds `async-std` feature for asynchronous file operations.
220  - Some performance optimizations.
221- 3.0.3
222  - Add default empty line on empty strings.
223  - Feature to append to existing `Ini` objects.
224  - Minor lint fixes.
225- 3.0.4 (**STABLE**)
226  - Adds pretty printing functionality
227  - Replaces `async-std` with `tokio` as the available async runtime
228  - *The `async-std` feature will be deprecated in a future release*
229
230### �� Future plans
231
232- Support for appending sections, coercing them as well.
233- Benchmarking against similar packages.
234