Expand description
Effortlessly embed config modules and access with any compatible types.
§Example
Below is a basic example illustrating how to declare a config module and access data from it.
use inline_config::{Config, path};
// Declare a config type containing literal sources.
#[derive(Config)]
// While only using literal sources,
// a format needs to be specified.
// Including a file from disk is also possible,
// see `examples/include.rs`.
#[config(format = "toml")]
// When there are multiple sources,
// latter ones overwrite former ones.
#[config(src = r#"
title = "TOML example"
[server]
owner = "Tom"
timeout = 2000
ports = [ 8000, 8001, 8002 ]
"#)]
#[config(src = r#"
[server]
timeout = 5000
"#)]
struct MyConfig;
// Use `Index`, `From` traits to access data.
// Different types may be accessible from a field.
let title: &str = MyConfig[path!(title)].into();
assert_eq!("TOML example", title);
let title: String = MyConfig[path!(title)].into();
assert_eq!("TOML example", title);
// A deeper path.
let owner: &str = MyConfig[path!(server.owner)].into();
assert_eq!("Tom", owner);
// Any numerical types.
let timeout: u32 = MyConfig[path!(server.timeout)].into();
assert_eq!(5000, timeout);
let timeout: f32 = MyConfig[path!(server.timeout)].into();
// A homogeneous array can be accessed as `Vec<T>`.
let ports: Vec<u64> = MyConfig[path!(server.ports)].into();
assert_eq!([8000, 8001, 8002].to_vec(), ports);See Config and path!() for specs on those macros.
§Compatible types
Internally, data from config sources are parsed into one of the seven variants: booleans, unsigned integers, signed integers, floats, strings, arrays, tables. Each of them has a specific storage representation, and have different compatible types.
| Representation variant | Compatible types |
|---|---|
| Boolean | bool |
| Unsigned Integer | i8, i16, i32, i64, i128, isize,u8, u16, u32, u64, u128, usize,f32, f64 |
| Signed Integer | i8, i16, i32, i64, i128, isize,f32, f64 |
| Float | f32, f64 |
| String | &str, String |
| Array | Vec<T> if homogeneous,User-defined structs deriving FromConfig with unnamed fields |
| Table | std::collections::BTreeMap<&str, T> if homogeneous,std::collections::BTreeMap<String, T> if homogeneous,indexmap::IndexMap<&str, T> if homogeneous*,indexmap::IndexMap<String, T> if homogeneous*,User-defined structs deriving FromConfig with named fields |
* Only available when enabling indexmap feature flag.
§Container types
Arrays and tables are both “containers” in the sense of containing children data, therefore you can use path!() to access children data.
The only difference between the two containers is that arrays have unnamed but ordered fields, while tables have named but unamed fields.
This suggests you should use indices when accessing a field of an array, but use names when accessing a field of a table.
Note that they are inhomogeneous in general (children are of different types).
You need to define custom types and derive FromConfig if you want to access structured data.
Define structs with unnamed fields to model an array, while structs with named fields to model a table.
Specially, in the case when they do contain homogeneous data,
arrays can be accessed as Vec<T>, and tables can be accessed as std::collections::BTreeMap<&str, T> or std::collections::BTreeMap<String, T>,
as long as the representation of children can be accessed as T.
For containers, this type compatibility comes with a recursive sense.
There’s a relevant concept from functional programming, known as transmogrifying.
§Feature flags
json- supports JSON file format. Enabled by default.yaml- supports YAML file format. Enabled by default.toml- supports TOML file format. Enabled by default.indexmap- enables preserving orders of tables.
Macros§
- Path
- The type version of
path!(). Used in type bounds. - path
- Constructs a path with which one accesses a nested-in piece of data from config.
Derive Macros§
- Config
- Attaches config data to a unit struct.
- From
Config - Defines a data structure that can be converted directly from a compatible container.