Dynamically-typed value type for libconfig data.
<p align="center">
<img src="https://gitlab.com/rustographer/serde-libconfigasaurus/-/raw/main/assets/arachnid.png" alt="arachnid logo" width="300" height="300" >
</p>
# **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
```rust,ignore
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
```rust
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
```rust
# use libconfig::Value;
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
```rust
# use libconfig::Value;
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
```rust
# use libconfig::Value;
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
```rust
# use libconfig::Value;
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
```rust
# use libconfig::Value;
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
```rust,ignore
// 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
```rust
# use libconfig::Value;
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
```rust
# use libconfig::Value;
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:
```rust
# use libconfig::Value;
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:
```rust
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:
```rust
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
```rust
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
1. **Use typed deserialization when possible** - It's faster and type-safe
2. **Use `Value` for unknown structures** - When config comes from external sources
3. **Check types before conversion** - Use `is_*()` methods before `as_*()` conversions
4. **Use `lookup()` for paths** - More convenient than chaining `get()` calls
5. **Handle Options** - All accessor methods return `Option` types
## Performance Notes
- `IndexMap` is 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)