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