serde-libconfigasaurus 0.3.2

Serde serialization and deserialization for libconfig format
Documentation
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)