# Configuration Utilities

## Purpose

VCollab applications read configuration from multiple sources: environment
variables, direct constructor arguments, and hardcoded defaults. Without a
shared utility layer, every package implements its own environment variable
parsing and configuration resolution, leading to inconsistent behavior.

The `vcti-config` package is built around three concepts:

- **Pure decoders** -- stateless `str -> T` functions for bool, int, float,
  string, path, and JSON
- **Field** -- a descriptor that pairs a decoder with a default, validator,
  and env var name
- **ConfigSchema** -- a declarative base class that resolves fields from
  environment variables and overrides, with full error collection
- **resolve_value** -- a standalone priority-chain function for use outside
  schemas
- **Structured errors** -- `DecodeError`, `MissingFieldError`,
  `ValidationError`, and `ConfigErrors` for actionable diagnostics

This package has **zero external dependencies**.

---

## Installation

```bash
# Latest main branch
pip install vcti-config



### In `requirements.txt`

```
vcti-config>=3.1.2
```

### In `pyproject.toml` dependencies

```toml
dependencies = [
    "vcti-config>=3.1.2",
]
```

---

## Quick Start

### Decode typed values from strings

```python
from vcti.config import decode_int, decode_bool

decode_int("8080")    # 8080
decode_bool("yes")    # True
decode_bool("maybe")  # raises DecodeError
```

### Resolve values from multiple sources

```python
from vcti.config import resolve_value

port = resolve_value(cli_args.port, env_port, default=8080)
```

### Read and decode an env var

```python
from vcti.config import read_env, decode_int, resolve_value

raw = read_env("PORT")
port = resolve_value(
    decode_int(raw) if raw else None,
    default=8080,
)
```

### Declare a configuration schema

```python
from vcti.config import ConfigSchema, Field

class AppConfig(ConfigSchema):
    ENV_PREFIX = "MYAPP_"
    debug = Field.bool(default=False)
    port  = Field.int(default=8080, validator=lambda p: 1 <= p <= 65535)
    name  = Field.str(default="myapp")

config = AppConfig.load()
config.debug  # bool
config.port   # int
config.name   # str
```

### Pass overrides for testing

```python
config = AppConfig.load(
    overrides={"port": 9999},
    env={"MYAPP_DEBUG": "true"},  # custom env dict -- no monkeypatching
)
```

### Handle all errors at once

```python
from vcti.config import ConfigErrors

try:
    config = AppConfig.load()
except ConfigErrors as exc:
    for err in exc.errors:
        print(f"{err.field_name}: {err}")
```

---

## Boolean Flag Values

| True values | False values |
|-------------|--------------|
| `1`, `true`, `yes`, `on`, `enabled` | `0`, `false`, `no`, `off`, `disabled` |

All comparisons are case-insensitive. Custom sets can be passed via
`make_bool_decoder()`.

---

## Field Types

| Constructor | Python type | Decoder |
|-------------|-------------|---------|
| `Field.bool()` | `bool` | `decode_bool` |
| `Field.str()` | `str` | `decode_str` |
| `Field.int()` | `int` | `decode_int` |
| `Field.float()` | `float` | `decode_float` |
| `Field.path()` | `Path` | `decode_path` |
| `Field.json()` | `Any` | `decode_json` |
| `Field(custom_fn)` | `T` | Any `Callable[[str], T]` |

All constructors accept: `default`, `validator`, `env_name`,
`description`, and `sensitive` (masks values in error messages).

---

## Public API

| Name | Module | Purpose |
|------|--------|---------|
| `read_env` | `decode` | Read + strip an env var, returns `None` if unset/blank |
| `decode_bool`, `decode_int`, `decode_float`, `decode_str`, `decode_path`, `decode_json` | `decode` | Pure `str -> T` decoders |
| `make_bool_decoder` | `decode` | Factory for custom boolean decoders |
| `Field` | `field` | Typed field descriptor with default, validator, `decode_env()` |
| `MISSING` | `field` | Sentinel for "no default provided" |
| `ConfigSchema` | `schema` | Declarative base class with `.load()` |
| `resolve_value` | `resolve` | Standalone priority-chain resolution |
| `ConfigError`, `DecodeError`, `MissingFieldError`, `ValidationError`, `ConfigErrors` | `errors` | Structured error hierarchy |

---

## Documentation

- [Design](docs/design.md) -- Three concepts, architecture, design decisions
- [Source Guide](docs/source-guide.md) -- File descriptions and execution flow traces
- [API Reference](docs/api.md) -- Autodoc for all modules
