#ini-parser #config-file #configuration #settings #macro

ini

A simple macro built on top of configparser to load and parse ini files. You can use this to write Rust programs which can be customized by end users easily.

14 stable releases

1.3.0 Aug 29, 2020
1.2.8 Jul 22, 2020
1.2.5 Jun 28, 2020

#269 in Configuration

Download history 572/week @ 2024-07-20 511/week @ 2024-07-27 548/week @ 2024-08-03 547/week @ 2024-08-10 409/week @ 2024-08-17 773/week @ 2024-08-24 1606/week @ 2024-08-31 1231/week @ 2024-09-07 627/week @ 2024-09-14 787/week @ 2024-09-21 886/week @ 2024-09-28 439/week @ 2024-10-05 591/week @ 2024-10-12 436/week @ 2024-10-19 525/week @ 2024-10-26 1794/week @ 2024-11-02

3,403 downloads per month
Used in 10 crates

MIT OR LGPL-3.0-or-later

18KB

ini

Build Status Crates.io Crates.io Released API docs Maintenance

This crate provides the ini! macro 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.

This is a simple macro utility built on top of configparser with no other dependencies built on Rust. For more advanced functions, you should use the configparser crate.

Quick Start

A basic ini-syntax file (we say ini-syntax files because the files don't need to be necessarily *.ini) looks like this:

[DEFAULT]
key1 = value1
pizzatime = yes
cost = 9

[topsecrets]
nuclear launch codes = topsecret

[github.com]
User = QEDK

Essentially, the syntax consists of sections, each of which can which contains keys with values.

Installation

You can install this easily via cargo by including it in your Cargo.toml file like:

[dependencies]
ini = "1.3.0"

The ini! macro

The ini! macro allows you to simply get a hashmap of type HashMap<String, HashMap<String, Option<String>>> for a list of files. It is planned to provide shell expansion and file-writing in the future:

#[macro_use]
extern crate ini;

fn main() {
  let map = ini!("...path/to/file");
  // Proceed to use normal HashMap functions on the map:
  let val = map["section"]["key"].clone().unwrap();

  // To load multiple files, just do:
  let (map1, map2, map3) = ini!("path/to/file1", "path/to/file2", "path/to/file3");
  // Each map is a cloned hashmap with no relation to other ones
}

If loading a file fails or the parser is unable to parse the file, the code will panic with an appropriate error. In case, you want to handle this gracefully, it's recommended you use the safe metavariable instead. This will make sure your code does not panic and instead exists as a Result<HashMap, String> type and let you deal with errors gracefully.

let map = ini!(safe "...path/to/file");
// Proceed to use normal HashMap functions on the map:
let val = map.unwrap()["section"]["key"].clone().unwrap();
// Note the extra unwrap here, which is required because our HashMap is inside a Result type.

The inistr! macro

The inistr! macro allows you to simply get a hashmap of type HashMap<String, HashMap<String, Option<String>>> for a list of strings.

#[macro_use]
extern crate ini;

fn main() {
  let configstring = "[section]
    key = value
    top = secret";
  let map = inistr!(configstring);
  // Proceed to use normal HashMap functions on the map:
  let val = map["section"]["top"].clone().unwrap();
  // The type of the map is HashMap<String, HashMap<String, Option<String>>>
  assert_eq!(val, "secret"); // value accessible!

  // To load multiple string, just do:
  let (map1, map2, map3) = inistr!(&String::from(configstring), configstring,  "[section]
    key = value
    top = secret");
  // Each map is a cloned hashmap with no relation to other ones
}

If loading a file fails or the parser is unable to parse the file, the code will panic with an appropriate error. In case, you want to handle this gracefully, it's recommended you use the safe metavariable instead. This will make sure your code does not panic and instead exists as a Result<HashMap, String> type and let you deal with errors gracefully.

let map = inistr!(safe strvariable_or_strliteral);
 // Proceed to use normal HashMap functions on the map:
let val = map.unwrap()["section"]["key"].clone().unwrap();
 // Note the extra unwrap here, which is required because our HashMap is inside a Result type.

Supported datatypes

configparser does not guess the datatype of values in configuration files and stores everything as strings, same applies to ini. If you need getters that parse the values for you, you might want to use the configparser crate instead. You can ofcourse just choose to parse the string values yourself.

let my_string = map["section"]["key"].clone().unwrap();
let my_int = my_string.parse::<i32>().unwrap();

Supported ini file structure

A configuration file can consist of sections, each led by a [section-name] header, followed by key-value entries separated by a =. By default, section names and key names are case-insensitive. All leading and trailing whitespace is removed from stored keys, values and section names. Key values can be omitted, in which case the key-value delimiter (=) may also be left out (but this is different from putting a delimiter, we'll explain it later). You can use comment symbols (; and # to denote comments). Keep in mind that key-value pairs or section headers cannot span multiple lines. Owing to how ini files usually are, this means that [, ], =, ; and # are special symbols (this crate will allow you to use ] sparingly).

Let's take for example:

[section headers are case-insensitive]
[   section headers are case-insensitive    ]
are the section headers above same? = yes
sectionheaders_and_keysarestored_in_lowercase? = yes
keys_are_also_case_insensitive = Values are case sensitive
;anything after a comment symbol is ignored
#this is also a comment
spaces in keys=allowed ;and everything before this is still valid!
spaces in values=allowed as well
spaces around the delimiter = also OK


[All values are strings]
values like this= 0000
or this= 0.999
are they treated as numbers? = no
integers, floats and booleans are held as= strings

[value-less?]
a_valueless_key_has_None
this key has an empty string value has Some("") =

    [indented sections]
        can_values_be_as_well = True
        purpose = formatting for readability
        is_this_same     =        yes
            is_this_same=yes

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 or property key) is the one that remains in the HashMap. The only bit of magic the API does is the section-less properties are put in a section called "default".

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the LGPL-3.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~73KB