nexus_rt/lib.rs
1//! # nexus-rt
2//!
3//! Runtime primitives for single-threaded, event-driven systems.
4//!
5//! `nexus-rt` provides the building blocks for constructing runtimes where
6//! user code runs as handlers dispatched over shared state. It is **not** an
7//! async runtime — there is no task scheduler, no work stealing, no `Future`
8//! polling. Instead, it provides:
9//!
10//! - **World** — [`World`] is a unified type-erased singleton store. Each
11//! registered type gets a dense index ([`ResourceId`]) for ~3-cycle
12//! dispatch-time access.
13//!
14//! - **Resources** — [`Res`] and [`ResMut`] are what users see in handler
15//! function signatures. They deref to the inner value transparently.
16//!
17//! - **Handlers** — The [`Param`] trait resolves state at build time
18//! and produces references at dispatch time. [`IntoHandler`] converts
19//! plain functions into [`Handler`] trait objects for type-erased dispatch.
20//!
21//! - **Pipeline** — [`PipelineStart`] begins a typed per-event composition
22//! chain. Steps transform data using `Option` and `Result` for flow
23//! control. [`Pipeline`] implements [`Handler`] for direct or boxed dispatch.
24//! [`BatchPipeline`] owns a pre-allocated input buffer and runs each item
25//! through the same chain independently — errors on one item don't affect
26//! subsequent items.
27//!
28//! - **DAG Pipeline** — [`DagStart`] builds a monomorphized data-flow graph
29//! with fan-out and merge. Topology is encoded in the type system — no
30//! vtable dispatch, no arena allocation. [`Dag`] implements [`Handler`]
31//! for direct or boxed dispatch. [`BatchDag`] owns a pre-allocated input
32//! buffer and runs each item through the same DAG independently.
33//!
34//! - **Installer** — [`Installer`] is the install-time trait for event sources.
35//! `install()` registers resources into [`WorldBuilder`] and returns a
36//! concrete poller whose `poll()` method drives the event lifecycle.
37//!
38//! - **Templates** — [`HandlerTemplate`] and [`CallbackTemplate`] resolve
39//! parameters once, then stamp out handlers via [`generate()`](HandlerTemplate::generate)
40//! by copying pre-resolved state (~1 cycle). Use when handlers are
41//! created repeatedly on the hot path (IO re-registration, timer
42//! rescheduling).
43//!
44//! - **Plugin** — [`Plugin`] is a composable unit of resource registration.
45//! [`WorldBuilder::install_plugin`] consumes a plugin to configure state.
46//!
47//! # Quick Start
48//!
49//! ```
50//! use nexus_rt::{WorldBuilder, ResMut, IntoHandler, Handler};
51//!
52//! let mut builder = WorldBuilder::new();
53//! builder.register::<u64>(0);
54//! let mut world = builder.build();
55//!
56//! fn tick(mut counter: ResMut<u64>, event: u32) {
57//! *counter += event as u64;
58//! }
59//!
60//! let mut handler = tick.into_handler(world.registry());
61//!
62//! handler.run(&mut world, 10u32);
63//!
64//! assert_eq!(*world.resource::<u64>(), 10);
65//! ```
66//!
67//! # Safety
68//!
69//! The low-level `get` / `get_mut` methods on [`World`] are `unsafe` and
70//! intended for framework internals. The caller must ensure:
71//!
72//! 1. The ID was obtained from the same builder that produced the container.
73//! 2. The type parameter matches the type registered at that ID.
74//! 3. No mutable aliasing — at most one `&mut T` exists per value at any time.
75//!
76//! User-facing APIs (`resource`, `resource_mut`, `Handler::run`) are fully safe.
77//!
78//! # Bevy Analogies
79//!
80//! nexus-rt borrows heavily from Bevy ECS's system model. If you know
81//! Bevy, these mappings may help:
82//!
83//! | nexus-rt | Bevy | Notes |
84//! |----------|------|-------|
85//! | [`Param`] | `SystemParam` | Two-phase init/fetch |
86//! | [`Res<T>`] | `Res<T>` | Shared resource read |
87//! | [`ResMut<T>`] | `ResMut<T>` | Exclusive resource write |
88//! | [`Local<T>`] | `Local<T>` | Per-handler state |
89//! | [`Handler<E>`] | `System` trait | Object-safe dispatch |
90//! | [`IntoHandler`] | `IntoSystem` | fn → handler conversion |
91//! | [`Plugin`] | `Plugin` | Composable registration |
92//! | [`World`] | `World` | Singletons only (no ECS) |
93//! | [`HandlerTemplate`] | *(no equivalent)* | Resolve-once, stamp-many |
94
95#![warn(missing_docs)]
96
97mod adapt;
98mod callback;
99mod catch_unwind;
100mod combinator;
101pub mod dag;
102mod driver;
103mod handler;
104#[cfg(feature = "mio")]
105pub mod mio;
106pub mod pipeline;
107mod plugin;
108mod resource;
109pub mod scheduler;
110pub mod shutdown;
111pub mod system;
112pub mod template;
113pub mod testing;
114#[cfg(feature = "timer")]
115pub mod timer;
116mod world;
117
118#[cfg(feature = "codegen-audit")]
119pub mod codegen_audit;
120
121pub use adapt::{Adapt, ByRef, Cloned, Owned};
122pub use callback::{Callback, IntoCallback};
123pub use catch_unwind::CatchAssertUnwindSafe;
124pub use combinator::{Broadcast, FanOut};
125pub use dag::{BatchDag, Dag, DagStart, resolve_arm};
126pub use driver::Installer;
127pub use handler::{CtxFree, Handler, HandlerFn, IntoHandler, Local, Param, RegistryRef};
128pub use pipeline::{
129 BatchPipeline, IntoStep, Pipeline, PipelineBuilder, PipelineOutput, PipelineStart, resolve_step,
130};
131pub use plugin::Plugin;
132pub use resource::{Res, ResMut};
133pub use scheduler::{MAX_SYSTEMS, SchedulerInstaller, SchedulerTick, SystemId, SystemScheduler};
134pub use shutdown::{Shutdown, ShutdownHandle};
135pub use system::{IntoSystem, System, SystemFn};
136pub use template::{
137 Blueprint, CallbackBlueprint, CallbackTemplate, HandlerTemplate, TemplatedCallback,
138 TemplatedHandler,
139};
140pub use world::{Registry, ResourceId, Sequence, World, WorldBuilder};
141
142/// Type alias for a boxed, type-erased [`Handler`].
143///
144/// Use `Virtual<E>` when you need to store heterogeneous handlers in a
145/// collection (e.g. `Vec<Virtual<E>>`). One heap allocation per handler.
146///
147/// For inline storage (no heap), see [`FlatVirtual`] (panics if handler
148/// doesn't fit) or [`FlexVirtual`] (inline with heap fallback). Both
149/// require the `smartptr` feature.
150pub type Virtual<E> = Box<dyn Handler<E>>;
151
152/// Type alias for an inline [`Handler`] using [`nexus_smartptr::Flat`].
153///
154/// Stores the handler inline (no heap allocation). Panics if the concrete
155/// handler doesn't fit in the buffer.
156#[cfg(feature = "smartptr")]
157pub type FlatVirtual<E, B = nexus_smartptr::B64> = nexus_smartptr::Flat<dyn Handler<E>, B>;
158
159/// Type alias for an inline [`Handler`] with heap fallback using [`nexus_smartptr::Flex`].
160///
161/// Stores inline if the handler fits, otherwise heap-allocates.
162#[cfg(feature = "smartptr")]
163pub type FlexVirtual<E, B = nexus_smartptr::B64> = nexus_smartptr::Flex<dyn Handler<E>, B>;
164
165#[cfg(feature = "mio")]
166pub use self::mio::{BoxedMio, MioConfig, MioDriver, MioInstaller, MioPoller, MioToken};
167
168#[cfg(all(feature = "mio", feature = "smartptr"))]
169pub use self::mio::{FlexMio, InlineMio};
170
171#[cfg(feature = "timer")]
172pub use timer::{BoxedTimers, Periodic, TimerConfig, TimerInstaller, TimerPoller, TimerWheel};
173
174#[cfg(all(feature = "timer", feature = "smartptr"))]
175pub use timer::{FlexTimerWheel, FlexTimers, InlineTimerWheel, InlineTimers};