Expand description
Dynamically-typed value type for libconfig data.
§Value API Documentation
The Value type provides a dynamically-typed representation of libconfig data.
§Overview
When you don’t know the structure of your configuration at compile time, you can deserialize into a Value and traverse it dynamically.
§Value Variants
ⓘ
pub enum Value {
Bool(bool), // Boolean value
Integer(i32), // 32-bit integer
Integer64(i64), // 64-bit integer
Float(f64), // Floating point number
String(String), // String value
Array(Vec<Value>), // Array (homogeneous sequence)
List(Vec<Value>), // List (heterogeneous sequence)
Group(Map), // Group (object/map)
}
pub type Map = IndexMap<String, Value>;§Usage Examples
§Basic Access
use libconfig::Value;
let config = r#"
{
name = "My App";
version = 1;
enabled = true;
}
"#;
let v = Value::from_str(config).unwrap();
// Index with square brackets
println!("{}", v["name"]); // "My App"
println!("{}", v["version"]); // 1
println!("{}", v["enabled"]); // true§Nested Access
let config = r#"
{
database = {
host = "localhost";
port = 5432;
};
}
"#;
let v = Value::from_str(config).unwrap();
// Access nested values
println!("{}", v["database"]["host"]); // "localhost"
println!("{}", v["database"]["port"]); // 5432§Array Access
let config = r#"
{
ports = [8080, 8081, 8082];
}
"#;
let v = Value::from_str(config).unwrap();
// Index into arrays
println!("{}", v["ports"][0]); // 8080
println!("{}", v["ports"][1]); // 8081§Path Lookup
let config = r#"
{
application = {
window = {
title = "My App";
};
};
}
"#;
let v = Value::from_str(config).unwrap();
// Use dotted path
if let Some(title) = v.lookup("application.window.title") {
println!("Title: {}", title); // "My App"
}§Type Checking Methods
let v = Value::Integer(42);
// Check type
assert!(v.is_integer()); // true
assert!(v.is_i32()); // true
assert!(v.is_number()); // true
assert!(!v.is_string()); // false
// Other type checks:
// - is_bool()
// - is_i64()
// - is_float()
// - is_string()
// - is_array()
// - is_list()
// - is_group()§Type Conversion Methods
let v = Value::Integer(42);
// Convert to native types
assert_eq!(v.as_i32(), Some(42));
assert_eq!(v.as_i64(), Some(42));
assert_eq!(v.as_f64(), Some(42.0));
let v = Value::String("hello".to_string());
assert_eq!(v.as_str(), Some("hello"));
let v = Value::Array(vec![Value::Integer(1), Value::Integer(2)]);
assert_eq!(v.as_array().unwrap().len(), 2);
// Conversion methods:
// - as_bool() -> Option<bool>
// - as_i32() -> Option<i32>
// - as_i64() -> Option<i64>
// - as_f64() -> Option<f64>
// - as_str() -> Option<&str>
// - as_array() -> Option<&Vec<Value>>
// - as_group() -> Option<&Map>§Access Methods
ⓘ
// Get by key (for groups)
if let Some(value) = v.get("database") {
println!("Found database config");
}
// Get by index (for arrays/lists)
if let Some(value) = v.get_index(0) {
println!("First element: {}", value);
}
// Get mutable reference
if let Some(value) = v.get_mut("name") {
*value = Value::String("New Name".to_string());
}§Iterating
§Over Group Fields
let config = r#"
{
features = {
auth = true;
cache = false;
};
}
"#;
let v = Value::from_str(config).unwrap();
if let Some(features) = v["features"].as_group() {
for (key, value) in features {
println!("{}: {}", key, value);
}
}§Over Array Elements
let config = r#"
{
servers = ["web-1", "web-2", "api-1"];
}
"#;
let v = Value::from_str(config).unwrap();
if let Some(servers) = v["servers"].as_array() {
for server in servers {
println!("Server: {}", server);
}
}§Type Comparisons
You can directly compare Value with native Rust types:
let v = Value::Integer(42);
assert!(v == 42);
let v = Value::String("hello".to_string());
assert!(v == "hello");
let v = Value::Bool(true);
assert!(v == true);§Serialization
Value implements Serialize, so you can convert it back to libconfig format:
use libconfig::Value;
let config = r#"{ name = "Test"; }"#;
let v = Value::from_str(config).unwrap();
// Serialize back to libconfig
let output = v.to_string().unwrap();
println!("{}", output);§Converting Between Value and Typed Structs
You can convert Value to a concrete type:
use serde::Deserialize;
use libconfig::{from_value, Value};
#[derive(Deserialize)]
struct DatabaseConfig {
host: String,
port: i32,
}
let config = r#"
{
database = {
host = "localhost";
port = 5432;
};
}
"#;
let v = Value::from_str(config).unwrap();
// Extract and convert the database config
let db_config: DatabaseConfig = from_value(v["database"].clone()).unwrap();
println!("Host: {}", db_config.host);
println!("Port: {}", db_config.port);§Error Handling
use libconfig::Value;
let config = r#"{ invalid syntax"#;
match Value::from_str(config) {
Ok(v) => println!("Success: {:?}", v),
Err(e) => eprintln!("Parse error: {}", e),
}§Best Practices
- Use typed deserialization when possible - It’s faster and type-safe
- Use
Valuefor unknown structures - When config comes from external sources - Check types before conversion - Use
is_*()methods beforeas_*()conversions - Use
lookup()for paths - More convenient than chainingget()calls - Handle Options - All accessor methods return
Optiontypes
§Performance Notes
IndexMapis used for groups (preserves insertion order, O(1) lookups)- Index access is O(1) for arrays
- Path lookup is O(n) where n is path depth
- All strings are owned (no zero-copy yet)