serde-libconfigasaurus 0.3.2

Serde serialization and deserialization for libconfig format
Documentation
<p align="center">
  <img src="https://gitlab.com/rustographer/serde-libconfigasaurus/-/raw/main/assets/libconfigasaurus.png" alt="libconfigasaurus logo" width="300" height="300" >
</p>

# **serde-libconfigasaurus**

A Rust [serde](https://serde.rs/) implementation for the [libconfig](https://hyperrealm.github.io/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 / `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` suffix explicitly (`100L`).

## Quick Start

```toml
[dependencies]
serde-libconfigasaurus = "0.3.2"
serde = { version = "1.0", features = ["derive"] }
```

### Typed Deserialization

```rust
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

```rust
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 serde serializer always wraps the top-level struct in { }.
// For braces-free output, use Config::new() + set() + Config::to_string().
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:

```rust
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

```rust ,no_run
use libconfig::Config;

let cfg = Config::from_file("settings.cfg").unwrap();
println!("{}", cfg["database.host"]);
```

## 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:

```rust
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)
cfg.set("ports.[0]", Value::Integer(8080));
assert_eq!(cfg["ports.[0]"], 8080);

// Write (auto-creates intermediate groups)
cfg.set("database.credentials.username", Value::String("admin".to_string()));
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]");
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

## Examples

```bash
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