nexus-rt 2.2.0

Single-threaded, event-driven runtime primitives with pre-resolved dispatch
Documentation
# Drivers

Drivers integrate external systems (timers, clocks, IO) into the poll loop.
They follow the **installer/poller** pattern: consumed at setup, return a
handle for runtime polling.

## The Pattern

```
Setup time:                          Runtime:

┌──────────────┐                    ┌──────────────┐
│  Installer   │                    │   Poller     │
│              │  install()         │              │
│  config,     │ ──────────────►    │  ResourceId, │
│  resources   │  registers         │  state       │
│              │  resources in      │              │
│  (consumed)  │  WorldBuilder      │  poll/sync   │
└──────────────┘                    └──────────────┘
```

The installer carries configuration and registers resources into the
WorldBuilder. It's consumed by `install()`, which returns the poller.
The poller holds pre-resolved `ResourceId`s and polls/syncs on each
loop iteration.

## Installer Trait

```rust
pub trait Installer {
    type Poller;

    /// Consume the installer, register resources, return the poller.
    fn install(self, world: &mut WorldBuilder) -> Self::Poller;
}
```

`install()` takes `self` by value — the installer is consumed. The
returned `Poller` is the runtime handle.

## Built-In Drivers

### Timer Driver

```rust
use nexus_rt::timer::{TimerInstaller, TimerPoller, TimerWheel};
use nexus_timer::WheelBuilder;

// Setup
let wheel = WheelBuilder::default().unbounded(64).build(Instant::now());
let mut timer: TimerPoller = wb.install_driver(TimerInstaller::new(wheel));

// Poll loop
timer.poll(&mut world, now);  // fires expired timers, dispatches handlers
```

The timer driver registers a `TimerWheel` resource. Handlers can schedule
timers via `ResMut<TimerWheel>`.

### Clock Driver

```rust
use nexus_rt::clock::{RealtimeClockInstaller, Clock};

// Setup
let mut clock = wb.install_driver(RealtimeClockInstaller::default());

// Poll loop
clock.sync(&mut world, now);  // writes Clock resource
```

Three variants: `RealtimeClockInstaller` (production), `TestClockInstaller`
(testing), `HistoricalClockInstaller` (replay). See [clock.md](clock.md).

## Building Your Own Driver

A driver for a hypothetical market data feed:

```rust
use nexus_rt::{Installer, Resource, ResourceId, WorldBuilder, World};

// The resource handlers will access
#[derive(Resource)]
pub struct MarketData {
    pub last_price: f64,
    pub sequence: u64,
}

// Installer — carries connection config
pub struct MarketDataInstaller {
    feed_url: String,
}

impl MarketDataInstaller {
    pub fn new(feed_url: String) -> Self {
        Self { feed_url }
    }
}

// Poller — holds ResourceId + connection state
pub struct MarketDataPoller {
    data_id: ResourceId,
    // connection: FeedConnection,  // your IO handle
}

impl Installer for MarketDataInstaller {
    type Poller = MarketDataPoller;

    fn install(self, world: &mut WorldBuilder) -> MarketDataPoller {
        let data_id = world.register(MarketData {
            last_price: 0.0,
            sequence: 0,
        });

        // Connect to feed, set up IO...

        MarketDataPoller {
            data_id,
            // connection: connect(self.feed_url),
        }
    }
}

impl MarketDataPoller {
    pub fn poll(&mut self, world: &mut World) {
        // Read from connection, update resource
        let data = unsafe { world.get_mut::<MarketData>(self.data_id) };
        // data.last_price = ...;
        // data.sequence += 1;
    }
}
```

Usage:
```rust
let mut wb = WorldBuilder::new();
let mut md = wb.install_driver(MarketDataInstaller::new("wss://...".into()));
let mut world = wb.build();

loop {
    let now = Instant::now();
    clock.sync(&mut world, now);
    md.poll(&mut world);           // updates MarketData resource
    timer.poll(&mut world, now);
}
```

Handlers access the market data naturally:
```rust
fn on_timer(data: Res<MarketData>, clock: Res<Clock>) {
    println!("price={} seq={} at {}", data.last_price, data.sequence, clock.unix_nanos());
}
```

## Registering Handlers in Drivers (Rust 2024)

If your driver accepts handlers via a method that takes `&Registry` and
stores an `impl Handler<E>`, the Rust 2024 `+ use<...>` capture rule
applies to any factory function the user passes handlers through.

For example, if a user builds a pipeline in a factory function and
registers it with your driver:

```rust
fn on_message<C: Config>(
    reg: &Registry,
) -> impl for<'a> Handler<Msg<'a>> + use<C> {
    PipelineBuilder::<Msg<'_>>::new()
        .then(decode::<C>, reg)
        .dispatch(process::<C>.into_handler(reg))
        .build()
}

// Usage:
let handler = on_message::<MyConfig>(wb.registry());
driver.register(handler);   // works — registry borrow ended
let world = wb.build();      // works — no outstanding borrows
```

Without `+ use<C>` on `on_message`, the `wb.registry()` borrow would
be held, and `wb.build()` would fail. Document this in your driver's
handler registration examples.

See [Handlers — Returning from Functions](handlers.md#returning-handlers-from-functions-rust-2024).

## Design Principles

1. **Installer is consumed** — configuration doesn't persist. The poller
   is the only runtime artifact.

2. **Pre-resolved ResourceIds** — the poller resolves once at install,
   dereferences at O(1) on every poll.

3. **Poller owns its poll signature**`sync(&mut self, &mut World, Instant)`
   for the clock, `poll(&mut self, &mut World, Instant)` for the timer,
   `poll(&mut self, &mut World)` for market data. No forced common signature.

4. **Drivers don't know about each other** — each driver registers its
   resource and polls independently. The poll loop is the only place
   where ordering is decided.