Top-level document wrapper for reading and writing libconfig data.
<p align="center">
<img src="https://gitlab.com/rustographer/serde-libconfigasaurus/-/raw/v0.4.0/assets/v0.4/triceratops.png" alt="triceratops logo" width="300" >
</p>
# **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
```rust
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
```rust
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
assert!(Config::from_str("{ name = \"My App\"; }").is_err());
```
### Parse Errors
Errors carry the failure position (1-based) and a typed kind:
```rust
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
```rust ,no_run
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:
```rust ,no_run
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.
```text
# 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()`), use
`Config::from_str_with_include_dir` / `Config::from_file_with_include_dir`:
```rust ,no_run
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:
```rust ,no_run
use libconfig::Config;
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:
```rust
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
```rust
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:
```rust
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:
```rust
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!`:
```rust
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):
```rust
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
```rust
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
```rust
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
```rust
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:
```rust
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:
```rust
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
```rust
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
```rust
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:
```rust
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):
```rust
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
| **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()`.