topcoat 0.0.3

A modular, batteries-included Rust web framework for server-rendered apps.
Documentation
# App context

Most apps need values that outlive any single request: a database pool, an HTTP client, a config struct loaded at startup. Topcoat exposes these through **app context**: register a value once on the router, then read it from any handler with `app_context(cx)`.

App context is keyed by Rust type. Each type can be registered at most once, and lookups are typed: ask for `&Database` and you get a `&Database`.

## Registering values

Build the router and chain `.app_context(value)` for every value you want to share:

```rust
use topcoat::router::{Router, RouterBuilderDiscoverExt};

pub fn router() -> Router {
    Router::builder()
        .discover()
        .app_context(Database::connect())
        .app_context(HttpClient::new())
        .build()
}
```

The value is stored under its concrete type. Registering two values of the same type panics: wrap them in newtypes if you need more than one of the same underlying type:

```rust
struct PrimaryDb(Database);
struct ReplicaDb(Database);

Router::builder()
    .app_context(PrimaryDb(Database::connect_primary()))
    .app_context(ReplicaDb(Database::connect_replica()))
    .build();
```

## Reading values

Inside any handler that has access to a `Cx`, call `app_context::<T>(cx)` to borrow the registered value:

```rust
use topcoat::{
    context::{Cx, app_context},
    Result,
    router::page,
    view::view,
};

#[page]
async fn user_profile(cx: &Cx) -> Result {
    let db: &Database = app_context(cx);
    let user = db.fetch_user(42).await;
    view! { <h1>"Hello, " (user.name) </h1> }
}
```

The lookup is keyed by `T`'s `TypeId`, so the type you ask for must exactly match the type you registered. Asking for a type that wasn't registered panics: this is a startup-time bug, not a runtime one.

## Requirements

The value type `T` must be `Any + Send + Sync`. There's no `'static` bound to write yourself: `Any` implies it.

App context is shared by reference across every request handled by the router, so values should be cheap to share (typically already wrapped in `Arc` internally, like a database pool or HTTP client) or trivially clonable.