# Config Resolver - LLM & Coding Agent Guide

`config-resolver` is a Rust crate that resolves configuration strings (like IP addresses, socket addresses, ports, and peer IDs) dynamically at runtime.

This guide provides LLMs and coding agents with the API reference, syntax specification, and common patterns to integrate and extend `config-resolver`.

---

## Capabilities & Syntax Spec

The config resolution specification strings can be structured as follows:

| Pattern | Type | Description | Example |
|---|---|---|---|
| **Static IP** | `IpAddr` | Standard IPv4 or IPv6 string. | `"127.0.0.1"`, `"::1"` |
| **Static Port** | `u32` / `u64` | Standard unsigned integer. | `"3000"` |
| **Static SocketAddr** | `SocketAddr` | Standard IP and Port separated by a colon. | `"127.0.0.1:3000"`, `"[::1]:3000"` |
| **Provider Variable** | `IpAddr` | Name of a registered `ConfigProvider`. | `"AWS_PUBLIC_IP"`, `"IPIFY"` |
| **Combined Socket** | `SocketAddr` | A provider variable + a static port (or another provider variable). | `"DO_PUBLIC_IP:3000"`, `"AWS_PRIVATE_IP:IPIFY"` |
| **Offset Calculation** | `u64` | Mathematical addition to a base value. Parses the base value as a `u64` and applies the offset. | `"CALC(DO_PUBLIC_IP+10000)"`, `"CALC(127.0.0.1+100)"` |

### Calculation Detail
The `CALC(BASE+OFFSET)` syntax parses the resolved value of `BASE` (e.g. `127.0.0.1` -> `0x7F000001` = `2130706433`), adds the integer `OFFSET`, and returns a `u64`. This is typically used to dynamically construct unique Peer IDs or offsets.

---

## Core API Reference

### 1. Types
* **`ConfigRegistry`**: The central coordinator containing registered `ConfigProvider`s. Use this to parse and resolve spec strings.
* **`ConfigUnit`**: Trait implemented by types that can be resolved. Standard implementors: `u32`, `u64`, `IpAddr`, `SocketAddr`.
* **`ConfigProvider`**: Trait for custom resolution backends.
* **`ResolvedValue`**: Enum of intermediate types (`U32`, `U64`, `Ip`, `SocketAddr`).
* **`Error`**: Crate error type.

### 2. Standard Functions
* **`create_default_registry() -> ConfigRegistry`**: Creates a registry pre-loaded with the default providers (`DO_PUBLIC_IP`, `DO_PRIVATE_IP`, `AWS_PUBLIC_IP`, `AWS_PRIVATE_IP`, `IPIFY`) if their respective Cargo features are enabled.

---

## Common Code Patterns

### Setup & Resolution
To resolve a specification string:

```rust
use std::net::{IpAddr, SocketAddr};
use config_resolver::create_default_registry;

async fn init_networking(ip_spec: &str, listen_spec: &str) -> Result<(), config_resolver::Error> {
    let registry = create_default_registry();
    
    // Resolve an IpAddr
    let ip: IpAddr = registry.resolve(ip_spec).await?;
    
    // Resolve a SocketAddr
    let listen_addr: SocketAddr = registry.resolve(listen_spec).await?;
    
    Ok(())
}
```

### Creating & Registering a Custom Provider
Implement the `ConfigProvider` trait:

```rust
use async_trait::async_trait;
use config_resolver::{ConfigRegistry, ConfigProvider, ResolvedValue, Error};
use std::net::IpAddr;

struct ConsulProvider;

#[async_trait]
impl ConfigProvider for ConsulProvider {
    fn name(&self) -> &str {
        "CONSUL_IP"
    }

    async fn resolve(&self) -> Result<ResolvedValue, Error> {
        // Dynamic lookup logic here
        let ip: IpAddr = "10.0.0.50".parse().unwrap();
        Ok(ResolvedValue::Ip(ip))
    }
}

fn setup() {
    let mut registry = ConfigRegistry::default();
    registry.register(Box::new(ConsulProvider));
    
    // Will resolve to 10.0.0.50:8080
    // let addr: SocketAddr = registry.resolve("CONSUL_IP:8080").await.unwrap();
}
```

### Integration with `clap` (CLI Parsing)
Accept string specifications directly from CLI args and resolve them at startup:

```rust
use clap::Parser;
use std::net::SocketAddr;
use config_resolver::create_default_registry;

#[derive(Parser)]
struct Args {
    /// Bind address (e.g. "127.0.0.1:3000", "DO_PRIVATE_IP:3000")
    #[arg(long, env, default_value = "127.0.0.1:3000")]
    bind_address: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args = Args::parse();
    let registry = create_default_registry();

    let socket: SocketAddr = registry.resolve(&args.bind_address).await?;
    println!("Server bound to: {}", socket);
    Ok(())
}
```

---

## Cargo Features

| Feature | Description | Default |
|---|---|---|
| `aws` | Enables AWS EC2 IMDSv2 metadata providers | Enabled |
| `digital-ocean` | Enables DigitalOcean metadata providers | Enabled |
| `ipify` | Enables public IP resolution via ipify.org | Enabled |

To build without any external HTTP/TLS dependencies:
```toml
config-resolver = { version = "0.1", default-features = false }
```
