Skip to main content

Crate libconfig

Crate libconfig 

Source
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.

libconfigasaurus logo

§serde-libconfigasaurus

A Rust serde implementation for the libconfig configuration format.

§Supported Types

All libconfig value types are supported:

libconfig typeRust / Value type
booleanbool / Value::Bool
integeri32 / Value::Integer
integer64i64 / Value::Integer64
floatf64 / Value::Float
stringString / 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_value convert between any serde type and Value
  • Config::to_file writes a document back to disk; from_reader/to_writer work with any Read/Write
  • Parse errors carry positions: err.line(), err.column()
  • Option fields round-trip naturally: None fields are omitted on write, and absent settings read back as None
  • set accepts anything convertible into a Value: cfg.set("port", 8080)
  • WriteOptions controls output style (float precision, scientific notation, tab width, : vs =, brace placement, separators) — all combinations emit grammar-valid text
  • Custom @include resolvers via Config::from_str_with_include_fn (wildcards, search paths — the config_set_include_func equivalent)
  • &str struct 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 (via Config::from_str / Config::from_file, with *_with_include_dir variants 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; \xNN escapes 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

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.