Working With Configuration Data
There are several different ways to work with configuration data. Configuration sources are normalized to a generic key-value pair format, which can then be merged and consumed universally; regardless of the original format.
Hierarchical Configuration Data
The Configuration API reads hierarchical configuration data by flattening the hierarchical data with the use of a delimiter in the configuration keys.
Consider the following appsettings.json
file:
{
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
},
"MyKey": "My appsettings.json Value",
"Logging": {
"LogLevel": {
"Default": "Information",
"App": "Warning",
"App.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
The following code displays several of the configurations settings:
use config::{*, ext::*};
fn main() {
let config = DefaultConfigurationBuilder::new()
.add_json_file("appsettings.json")
.build()
.unwrap();
let my_key_value = config.get("MyKey").unwrap().as_str();
let title = config.get("Position:Title").unwrap().as_str();
let name = config.section("Position").get("Name").unwrap().as_str();
let default_log_level = config.get("Logging:LogLevel:Default").unwrap().as_str();
println!("MyKey value: {}\n\
Title: {}\n\
Name: {}\n\
Default Log Level: {}",
my_key_value,
title,
name,
default_log_level);
}
The preferred way to read hierarchical configuration data is using the Options pattern provided by the more-options crate. The section
and children
methods are available to isolate sections and children of a section in the configuration data.
Configuration Keys and Values
Configuration keys:
- Are case-insensitive; for example,
ConnectionString
andconnectionstring
are treated as equivalent keys. - If a key and value is set in more than one configuration providers, the value from the last provider added is used.
- Hierarchical keys
- Within the Configuration API, a colon separator (
:
) works on all platforms. - In environment variables, a colon separator may not work on all platforms. A double underscore,
__
, is supported by all platforms and is automatically converted into a colon:
.
- Within the Configuration API, a colon separator (
- The
ConfigurationBinder
supports binding arrays to objects using array indices in configuration keys.
Configuration values:
- Are strings
- Null values can't be stored in configuration or bound to objects
Get Value
The get_value
and get_value_or_default
methods extract a single value from configuration with a specified key and converts it to the specified type.
use config::{*, ext::*};
fn main() {
let config = DefaultConfigurationBuilder::new()
.add_json_file("settings.json")
.build()
.unwrap();
let number: Option<u8> = config.get_value("NumberKey").unwrap().unwrap_or(99);
let flag: bool = config.get_value_or_default("Enabled").unwrap();
println!("Number = {}", number);
println!("Flag = {}", flag);
}
In the preceding code, if NumberKey
isn't found in the configuration, the default value of 99
is used. If Enabled
isn't found in the configuration, it will default to false
, which is the Default::default()
for bool
.
Section, Children, and Exists
For the examples that follow, consider the following MySubsection.json
file:
{
"section0": {
"key0": "value00",
"key1": "value01"
},
"section1": {
"key0": "value10",
"key1": "value11"
},
"section2": {
"subsection0": {
"key0": "value200",
"key1": "value201"
},
"subsection1": {
"key0": "value210",
"key1": "value211"
}
}
}
Section
section
returns a configuration subsection with the specified subsection key.
The following code returns values for section1
:
let section = config.section("section1");
println!("section1:key0: {}\n\
section1:key1: {}",
section.get("key0").unwrap().as_str(),
section.get("key1").unwrap().as_str());
The following code returns values for section2:subsection0
:
let section = config.section("section2:subsection0");
println!("section2:subsection0:key0: {}\n\
section2:subsection0:key0: {}",
section.get("key0").unwrap().as_str(),
section.get("key1").unwrap().as_str());
If a matching section isn't found, an empty ConfigurationSection
is returned.
Children and Exists
The following code calls children
and returns values for section2:subsection0
:
let section = config.section("section2");
if section.exists() {
for subsection in section.children() {
let key1 = format!("{}:key0", section.key());
let key2 = format!("{}:key1", section.key());
println!("{} value: {}\n\
{} value: {}",
&key1,
&key2,
section.get(&key1).unwrap().as_str(),
section.get(&key2).unwrap().as_str());
}
} else {
println!("section2 does not exist.");
}
println!("section1:key0: {}\n\
section1:key1: {}",
section.get("key0").unwrap().as_str(),
section.get("key1").unwrap().as_str());
The preceding code uses the exists
extension to verify the section exists.