Expand description
Top-level document wrapper for reading and writing libconfig data.
§Config API Documentation
The Config struct is the primary entry point for working with libconfig data. It wraps the root Value::Group of a configuration document. Per the grammar (<configuration> ::= <setting-list> | <empty>), the top level of a document is a list of settings — braces delimit nested groups only.
§Overview
Config wraps a private root Value::Group. It derefs to Value, so all
Value methods (indexing, lookup, set, remove, type checks) work directly
on a Config; Config::into_value() recovers ownership of the root, and
Config::try_from(value) wraps an existing group (non-groups are rejected).
Config implements Serialize and Deserialize, so it can itself be used
as a serde target: libconfig::from_str::<Config>(...) succeeds only for
documents whose root is a group.
§Creating a Config
§Empty Config
use libconfig::Config;
let mut config = Config::new();
config.set("name", "My App").unwrap();
config.set("version", 1).unwrap();
config.set("debug", false).unwrap();
assert_eq!(config["name"], "My App");
assert_eq!(config["version"], 1);Config::new() creates an empty configuration group. set accepts
anything convertible into a Value and validates the setting name.
§Parsing a String
use libconfig::Config;
// A configuration is a list of settings at the top level
let config = Config::from_str("name = \"My App\";\nversion = 1;").unwrap();
assert_eq!(config["name"], "My App");
// Braces around the top level are not part of the grammar
// (<configuration> ::= <setting-list> | <empty>) and are rejected;
// braces delimit nested groups only
assert!(Config::from_str("{ name = \"My App\"; }").is_err());§Parse Errors
Errors carry the failure position (1-based) and a typed kind:
use libconfig::{Config, ErrorKind};
let err = Config::from_str("a = 1;\nb = @;\n").unwrap_err();
assert_eq!(err.line(), Some(2));
assert_eq!(err.column(), Some(5));
assert_eq!(err.to_string(), "syntax error at line 2, column 5");
assert!(matches!(err.kind(), ErrorKind::Syntax));§Reading a File
use libconfig::Config;
let config = Config::from_file("settings.cfg").unwrap();
println!("{}", config["database.host"]);Config::from_file reads the file and parses it with Config::from_str.
The inverse, Config::to_file(path), serializes the document and writes it
(with a trailing newline) to the given path:
use libconfig::Config;
let mut config = Config::new();
config.set("name", "svc").unwrap();
config.to_file("settings.cfg").unwrap();§Include Directives
Config::from_str and Config::from_file process @include directives per
section 2.11 of the libconfig manual: each directive is replaced by the
contents of the named file before parsing.
# file: test.cfg
info: {
name = "Winston Churchill";
@include "quote.cfg"
country = "UK";
};Rules, matching the C library:
- A directive must appear on its own line:
@include "path"(trailing comments are allowed). Directives inside/* ... */comments are ignored. - Backslashes and double quotes in the path are escaped as
\\and\". - Includes may be nested at most 10 levels deep; exceeding the limit
produces
ErrorKind::Include. - Relative paths are resolved against the current working directory by
default. To resolve them against a specific directory instead (the
equivalent of
config_set_include_dir()), useConfig::from_str_with_include_dir/Config::from_file_with_include_dir:
use libconfig::Config;
let config = Config::from_file_with_include_dir("test.cfg", "/etc/myapp").unwrap();For full control (the equivalent of config_set_include_func()), a custom
resolver maps each directive path to the ordered list of files to inline —
enabling wildcard expansion, search paths, or variable substitution:
use libconfig::Config;
let config = Config::from_file_with_include_fn("test.cfg", |path| {
// e.g. expand "conf.d/*.cfg" with your favorite glob crate
Ok(vec![std::path::PathBuf::from(path)])
}).unwrap();Files produced by the resolver are scanned for nested @include directives
(resolved by the same function), up to the 10-level limit.
Note that the serde-level libconfig::from_str does not process
@include directives; include handling is a Config feature.
§Wrapping an Existing Value
TryFrom<Value> wraps a group value as a document; non-group values are
rejected, since a configuration is a group of settings:
use libconfig::{Config, Value};
let v = Value::from_str("name = \"test\";").unwrap();
let config = Config::try_from(v).unwrap();
assert_eq!(config["name"], "test");
assert!(Config::try_from(Value::Integer(42)).is_err());§Default
use libconfig::Config;
let config = Config::default(); // Same as Config::new()
assert!(config.is_group());
assert_eq!(config.as_group().unwrap().len(), 0);§Serialization
§Config::to_string
Serializes the config to a libconfig string. The top level is an implicit setting list; nested groups are brace-delimited:
use libconfig::Config;
let config = Config::from_str("name = \"test\";\nversion = 42;").unwrap();
let output = config.to_string().unwrap();
assert_eq!(output, "name = \"test\";\nversion = 42;");§Write Options
WriteOptions mirrors the C writer’s formatting knobs (manual chapter 3).
Every combination produces grammar-valid output; defaults keep this crate’s
canonical style:
use libconfig::{Config, WriteOptions};
let config = Config::from_str("port = 80; db = { host = \"h\"; };").unwrap();
// The C library's cosmetic style: ':' for groups, '{' on its own line
let opts = WriteOptions::new()
.colon_assignment_for_groups(true)
.open_brace_on_separate_line(true);
let text = config.to_string_with_options(&opts).unwrap();
assert_eq!(text, "port = 80;\ndb :\n{\n host = \"h\";\n};");Available options: float_precision(n) (fixed digits; default is the
shortest form that round-trips exactly), scientific_notation(bool),
tab_width(n) (0 = tabs), semicolon_separators(bool),
colon_assignment_for_groups(bool), colon_assignment_for_non_groups(bool),
and open_brace_on_separate_line(bool). Config::to_file_with_options and
crate-level to_string_with_options accept the same struct.
§Display
Config implements Display, so it can be used with format! and println!:
use libconfig::Config;
let config = Config::from_str("x = 10; y = 20;").unwrap();
println!("{}", config);
// x = 10;
// y = 20;§Serde Serialize
Config implements Serialize. Per the configuration file grammar, the top
level of a document is an implicit setting list, so serde serialization
produces no outer braces (braces only delimit nested groups):
use libconfig::Config;
let config = Config::from_str("a = 1; b = 2;").unwrap();
// Serde serialization writes the grammar's implicit top-level format
let serde_output = libconfig::to_string(&config).unwrap();
assert_eq!(serde_output, "a = 1;\nb = 2;");
// Config::to_string produces the same implicit format
let format_output = config.to_string().unwrap();
assert!(!format_output.starts_with('{'));§Reading Values
Since Config derefs to Value, all Value access methods work directly:
§Indexing
use libconfig::Config;
let config = Config::from_str("name = \"test\"; ports = [80, 443];").unwrap();
assert_eq!(config["name"], "test");
assert_eq!(config["ports"][0], 80);§Path Lookup
use libconfig::Config;
let config = Config::from_str(r#"
database = {
host = "localhost";
credentials = {
username = "admin";
};
};
"#).unwrap();
assert_eq!(
config.lookup("database.credentials.username").unwrap().as_str(),
Some("admin")
);
// Bracket notation for array indices
assert_eq!(
config.lookup("database.host").unwrap().as_str(),
Some("localhost")
);§Type Checks and Conversions
use libconfig::Config;
let config = Config::from_str("count = 42; name = \"test\";").unwrap();
assert!(config["count"].is_i32());
assert_eq!(config["count"].as_i32(), Some(42));
assert_eq!(config["name"].as_str(), Some("test"));
// Config itself is always a group
assert!(config.is_group());§Writing Values
§Setting Values
set takes anything convertible into a Value; explicit Value variants
are only needed for aggregates:
use libconfig::{Config, Value};
let mut config = Config::new();
// Scalars convert automatically
config.set("name", "My App").unwrap();
config.set("version", 1).unwrap();
config.set("ratio", 0.75).unwrap();
config.set("debug", true).unwrap();
config.set("big_number", 100000000000i64).unwrap();
// Arrays and lists are built from Values
config.set("ports", Value::Array(vec![80.into(), 443.into()])).unwrap();
// Intermediate groups are created automatically
config.set("database.host", "localhost").unwrap();
config.set("database.port", 5432).unwrap();
assert_eq!(config["database.host"], "localhost");
assert_eq!(config["database.port"], 5432);set returns a Result: names are validated against the grammar
([A-Za-z*][-A-Za-z0-9_*]*) and traversal failures are typed:
use libconfig::{Config, ErrorKind};
let mut config = Config::new();
assert!(matches!(
config.set("my key", 1).unwrap_err().kind(),
ErrorKind::InvalidSettingName(_)
));
config.set("scalar", 5).unwrap();
assert!(matches!(
config.set("scalar.child", 1).unwrap_err().kind(),
ErrorKind::PathTraversal(_)
));§Modifying Existing Values
use libconfig::{Config, Value};
let mut config = Config::from_str("name = \"old\"; version = 1;").unwrap();
// Overwrite via set
config.set("name", "new").unwrap();
assert_eq!(config["name"], "new");
// Overwrite via lookup_mut
*config.lookup_mut("version").unwrap() = Value::Integer(2);
assert_eq!(config["version"], 2);§Removing Values
use libconfig::{Config, Value};
let mut config = Config::from_str("a = 1; b = 2; c = 3;").unwrap();
let removed = config.remove("b");
assert_eq!(removed, Some(Value::Integer(2)));
assert!(config.lookup("b").is_none());
assert_eq!(config["a"], 1);
assert_eq!(config["c"], 3);§Round-Trip Preservation
Config preserves key ordering through round-trips:
use libconfig::Config;
let input = "zebra = 1;\napple = 2;\nmonkey = 3;";
let config = Config::from_str(input).unwrap();
let output = config.to_string().unwrap();
assert_eq!(output, input); // Exact match, order preserved§Indentation
Nested groups are indented with 2 spaces per level by default (see
WriteOptions::tab_width to change this):
use libconfig::Config;
let mut config = Config::new();
config.set("server.listen.host", "0.0.0.0").unwrap();
config.set("server.listen.port", 8080).unwrap();
let output = config.to_string().unwrap();
// server = {
// listen = {
// host = "0.0.0.0";
// port = 8080;
// };
// };§Config vs Value
Config | Value | |
|---|---|---|
| Purpose | Document-level wrapper | Individual data values |
| Root | Always a group (setting list) | Any value type |
from_str | Requires a document (settings) | Also parses bare values |
| Serialization | to_string / to_file (+ _with_options) | libconfig::to_string(&value) |
| Construction | Config::new(), Config::from_str() | Value::from(42), "x".into(), variants |
| File I/O | Config::from_file() + @include | No |
Use Config when working with complete configuration documents. Use Value variants when constructing individual values to pass to Config::set().