# dcontext

Distributed context propagation for Rust.

`dcontext` provides a scoped, type-safe key–value store that travels with the
execution flow — across function calls, async/sync boundaries, thread spawns,
and even process boundaries via serialization.

## Features

- **Scoped context** — Enter/leave scopes with automatic rollback (RAII guards)
- **Type-safe** — Compile-time checked access via generics over `Any` storage
- **Cross-thread** — Snapshot and attach context when spawning threads
- **Cross-async** — Helpers for propagating context across Tokio tasks
- **Runtime-agnostic async** — `ContextFuture` poll-wrapper works with any executor
- **Serializable** — Serialize context to bytes/string for cross-process propagation
- **Local-only entries** — Non-serializable context that stays within the process
- **Version migration** — Rolling upgrades with automatic old→new schema conversion
- **Scope chain** — Named scopes with queryable distributed call chain (`scope_chain()`)
- **O(1) lookups** — Index-based read cache for fast context access

## Quick Start

```toml
[dependencies]
dcontext = "0.3"
```

```rust
use dcontext::{register, enter_scope, enter_named_scope, get_context, set_context, scope_chain};
use serde::{Serialize, Deserialize};

#[derive(Clone, Default, Debug, Serialize, Deserialize)]
struct RequestId(String);

fn main() {
    register::<RequestId>("request_id");

    {
        let _guard = enter_named_scope("ingress");
        set_context("request_id", RequestId("req-123".into()));

        handle_request(); // sees "req-123"

        // Query the scope chain
        let chain = scope_chain(); // e.g. ["ingress"]
        println!("scope chain: {:?}", chain);
    }
    // scope reverted — request_id is back to default
}
```

## Architecture

```
┌─────────────────────────────────────────┐
│           Application Code              │
├─────────────────────────────────────────┤
│  register / get_context / set_context   │
├─────────────────────────────────────────┤
│  Scope Tree  │  Registry  │  Snapshot   │
├──────────────┼────────────┼─────────────┤
│  Thread-local storage / Task-local      │
└─────────────────────────────────────────┘
```

### Key Concepts

- **Context** — A `HashMap<String, Any>` of named, type-erased values
- **Scope** — A stack frame that overlays the parent; changes revert on exit
- **ScopeGuard** — RAII guard returned by `enter_scope()` / `enter_named_scope()`; drops → reverts
- **Scope Chain** — Ordered list of named scope names (local + remote prefix); query with `scope_chain()`
- **Snapshot** — A captured, cloneable, sendable copy of the current context
- **Registry** — Global type registration for serialization support
- **ContextKey\<T\>** — Optional typed key wrapper for compile-time safe access

## Integration Crates

| Crate | Description |
|-------|-------------|
| [dcontext-dactor](dcontext-dactor/) | Automatic context propagation through [dactor](https://crates.io/crates/dactor) actor messages |
| [dcontext-tracing](dcontext-tracing/) | Automatic context scoping via [tracing](https://crates.io/crates/tracing) spans |

## Cargo Features

| Feature | Default | Description |
|---------|---------|-------------|
| `tokio` | yes | Tokio task-local storage, `scope_async`, and async spawn helpers |
| `base64` | yes | Base64 string serialization for HTTP headers/gRPC metadata |
| `context-key` | yes | `ContextKey<T>` typed key wrapper for compile-time safe access |
| `context-future` | no | `ContextFuture` poll-wrapper for runtime-agnostic async (non-Tokio executors) |

## Documentation

- **[Usage Guide](docs/usage-guide.md)** — Comprehensive guide covering all features with examples
- **[Design Document](docs/dcontext-design.md)** — Internal architecture and design decisions

## Samples

Run any sample with `cargo run --bin <name>`:

| Sample | Description |
|--------|-------------|
| `basic_scope` | Core get/set/scope API |
| `cross_thread` | Thread propagation via `spawn_with_context` |
| `async_tasks` | Tokio propagation via `with_context` |
| `async_scopes` | `scope_async` across `.await` points |
| `cross_process` | Serialization (bytes/base64) |
| `typed_keys` | `ContextKey<T>` type safety |
| `macros` | `register_contexts!` & `with_scope!` |
| `worker_pool` | Context-aware work dispatch |
| `feature_flags` | Per-request feature flag overrides |
| `size_limits` | `set_max_context_size` cap |
| `tracing_scopes` | dcontext-tracing integration |
| `scope_chain` | Named scopes and scope chain query |
| `dactor_propagation` | dcontext-dactor propagation flow |

## License

MIT
