Expand description
Serde serialization and deserialization for libconfig format
This crate provides support for serializing and deserializing Rust data structures to and from the libconfig configuration file format.
§serde-libconfigasaurus
A Rust serde implementation for the libconfig configuration format.
§Supported Types
All libconfig value types are supported:
| libconfig type | Rust / Value type |
|---|---|
| boolean | bool / Value::Bool |
| integer | i32 / Value::Integer |
| integer64 | i64 / Value::Integer64 |
| float | f64 / Value::Float |
| string | String / Value::String |
array [...] | Vec<T> / Value::Array (homogeneous scalars) |
list (...) | tuples, Vec of aggregates / Value::List (heterogeneous values) |
group {...} | structs / Value::Group (named settings) |
Integer literals support decimal, hex (0xFF), binary (0b1010), and octal (0o17 / 0q17) formats. Values exceeding the 32-bit range are automatically promoted to 64-bit, or use the L / LL suffix explicitly (100L).
Rust enums round-trip too: unit variants are written as strings ("Variant"), and data-carrying variants as single-setting groups ({ Variant = value; }).
§Quick Start
[dependencies]
serde-libconfigasaurus = "0.4"
serde = { version = "1.0", features = ["derive"] }§Typed Deserialization
use serde::Deserialize;
use libconfig::from_str;
#[derive(Debug, Deserialize)]
struct Server {
hostname: String,
port: i32,
ssl: bool,
}
let config = r#"
hostname = "localhost";
port = 8080;
ssl = true;
"#;
let server: Server = from_str(config).unwrap();§Serialization
use serde::Serialize;
use libconfig::to_string;
#[derive(Serialize)]
struct AppSettings {
name: String,
version: i32,
}
let settings = AppSettings {
name: "My App".to_string(),
version: 1,
};
// The top level of a libconfig document is an implicit setting list;
// braces only appear around nested groups.
let output = to_string(&settings).unwrap();
// name = "My App";
// version = 1;§Dynamic Config
When you don’t know the structure at compile time, use Config for dynamic access:
use libconfig::Config;
let cfg = Config::from_str(r#"
title = "My HTTP server";
listen_ports = [80, 443];
misc = {
owner = "Chuck Norris";
contact = {
phone = "415-256-9999";
emails = ["chuck@norris.com", "chuck.norris@gmail.com"];
};
};
"#).unwrap();
// Index with dotted paths
assert_eq!(cfg["title"].as_str(), Some("My HTTP server"));
assert_eq!(cfg["listen_ports.0"].as_i32(), Some(80));
assert_eq!(cfg["misc.contact.phone"].as_str(), Some("415-256-9999"));§Reading from a File
use libconfig::Config;
let cfg = Config::from_file("settings.cfg").unwrap();
println!("{}", cfg["database.host"]);§More API
to_value/from_valueconvert between any serde type andValueConfig::to_filewrites a document back to disk;from_reader/to_writerwork with anyRead/Write- Parse errors carry positions:
err.line(),err.column() Optionfields round-trip naturally:Nonefields are omitted on write, and absent settings read back asNonesetaccepts anything convertible into aValue:cfg.set("port", 8080)WriteOptionscontrols output style (float precision, scientific notation, tab width,:vs=, brace placement, separators) — all combinations emit grammar-valid text- Custom
@includeresolvers viaConfig::from_str_with_include_fn(wildcards, search paths — theconfig_set_include_funcequivalent) &strstruct fields deserialize zero-copy when the string needs no escape decoding or concatenation
§Setting Paths
Values can be read, written, and removed using libconfig-style dotted paths. Array and list elements are addressed with bracket notation ([index]) or bare numeric segments:
use libconfig::{Config, Value};
let mut cfg = Config::from_str(r#"ports = [80, 443];"#).unwrap();
// Read
assert_eq!(cfg.lookup("ports.[0]").unwrap().as_i32(), Some(80));
assert_eq!(cfg.lookup("ports.0").unwrap().as_i32(), Some(80)); // bare numeric also works
// Write (set existing) — anything convertible into a Value works
cfg.set("ports.[0]", 8080).unwrap();
assert_eq!(cfg["ports.[0]"], 8080);
// Write (auto-creates intermediate groups)
cfg.set("database.credentials.username", "admin").unwrap();
assert_eq!(cfg["database.credentials.username"], "admin");
// Remove (returns the removed value)
let mut cfg = Config::from_str(r#"a = 1; b = 2; c = 3;"#).unwrap();
let removed = cfg.remove("b");
assert_eq!(removed, Some(Value::Integer(2)));
assert!(cfg.lookup("b").is_none());
// Remove array element (subsequent elements shift down)
let mut cfg = Config::from_str(r#"items = [10, 20, 30];"#).unwrap();
cfg.remove("items.[0]").unwrap();
assert_eq!(cfg["items.[0]"], 20);§Format Features
- Top-level implicit groups (no outer braces required)
- Case-insensitive booleans (
true,True,TRUE,FaLsE) - Adjacent string concatenation (
"hello" " world"->"hello world") - Comments:
#,//,/* */ - Setting separators: semicolons, commas, or whitespace (all optional)
- Both
=and:as key-value delimiters @include "file.cfg"directives (viaConfig::from_str/Config::from_file, with*_with_include_dirvariants to set the include directory)
§Spec Conformance
Parsing and serialization follow the libconfig manual (v1.8.2), including its Appendix B grammar:
- The document root is a setting list (
<configuration> ::= <setting-list> | <empty>); top-level braces are rejected on parse and never written — braces delimit nested groups only - Setting names must match
[A-Za-z*][-A-Za-z0-9_*]*(validated on read and write) - Duplicate setting names within a group are rejected
- Arrays may only contain scalar values of a single type; anything else
belongs in a list (serialization picks
[...]vs(...)automatically) - Out-of-range integer conversions and float-to-integer coercions are errors, not silent truncations; non-finite floats refuse to serialize
- Strings must be double-quoted;
\xNNescapes take exactly two hex digits
The manual (including its Appendix B grammar) is the sole authority for this crate’s behavior. Output has additionally been cross-checked against the C libconfig library as a comparison point; where an installed C version disagrees with the manual (e.g. pre-1.8 versions lacking binary/octal literals, trailing commas, or 64-bit auto-promotion), the manual wins.
Documented deviations: \xNN escapes ≥ 0x80 map to Unicode code points
(Rust strings cannot hold bare bytes), integers are accepted where floats are
expected (lossless), and the serde-level from_str does not process
@include directives (use Config for that).
§Examples
cargo run --example basic # Typed serialization/deserialization round-trip
cargo run --example config # Dynamic Config type: get/set all libconfig types
cargo run --example streaming # Tokio TCP stream of libconfig packets§License
MIT OR Apache-2.0
§Further Reading
config_api— full reference for theConfigdocument wrappervalue_api— full reference for the dynamicValuetype
Re-exports§
pub use de::Deserializer;pub use de::from_reader;pub use de::from_str;pub use error::Error;pub use error::ErrorKind;pub use error::Result;pub use ser::Serializer;pub use ser::WriteOptions;pub use ser::to_string;pub use ser::to_string_with_options;pub use ser::to_writer;pub use value::Config;pub use value::Map;pub use value::Value;pub use value::from_value;pub use value::to_value;pub use indexmap;
Modules§
- config_
api - Top-level document wrapper for reading and writing libconfig data.
- de
- Deserializer for libconfig format
- error
- Error types for libconfig serialization and deserialization
- ser
- Serializer for libconfig format
- value
- A dynamically-typed representation of libconfig data
- value_
api - Dynamically-typed value type for libconfig data.