Skip to main content

nexus_rt/
world.rs

1//! Type-erased singleton resource storage.
2//!
3//! [`World`] is a unified store where each resource type gets a direct pointer
4//! ([`ResourceId`]) for O(1) dispatch-time access. Registration happens through
5//! [`WorldBuilder`], which freezes into an immutable [`World`] container via
6//! [`build()`](WorldBuilder::build).
7//!
8//! The type [`Registry`] maps types to [`ResourceId`] pointers. It is shared
9//! between [`WorldBuilder`] and [`World`], and is passed to [`Param::init`] and
10//! [`IntoHandler::into_handler`](crate::IntoHandler::into_handler) so that handlers can resolve their parameter
11//! state during driver setup — before or after `build()`.
12//!
13//! # Lifecycle
14//!
15//! ```text
16//! let mut builder = WorldBuilder::new();
17//! builder.register::<PriceCache>(value);
18//! builder.register::<TimerDriver>(value);
19//!
20//! // Drivers can resolve handlers against builder.registry()
21//! // before World is built.
22//!
23//! let world = builder.build();  // → World (frozen)
24//! ```
25//!
26//! After `build()`, the container is frozen — no inserts, no removes. All
27//! [`ResourceId`] values are valid for the lifetime of the [`World`] container.
28
29use std::any::{TypeId, type_name};
30use std::cell::Cell;
31#[cfg(debug_assertions)]
32use std::cell::UnsafeCell;
33use std::marker::PhantomData;
34use std::ptr::NonNull;
35use std::sync::Arc;
36use std::sync::atomic::AtomicBool;
37
38use rustc_hash::FxHashMap;
39
40// =============================================================================
41// Debug-mode aliasing detection
42// =============================================================================
43
44/// Tracks resource accesses within a single Param::fetch phase to detect
45/// aliasing violations at runtime in debug builds.
46///
47/// Dispatch macros call [`World::clear_borrows`] then
48/// [`World::track_borrow`] for each resource fetched. If the same resource
49/// is fetched twice within a phase, we panic with a diagnostic. This catches
50/// framework bugs where two params in the same handler resolve to the same
51/// resource — something [`Registry::check_access`] catches at construction
52/// time, but this catches dynamically for dispatch paths (like [`Opaque`](crate::Opaque)
53/// closures) that bypass static analysis.
54///
55/// Only active during the narrow `Param::fetch` window — safe API methods
56/// are not tracked.
57///
58/// Completely compiled out in release builds — zero bytes, zero branches.
59#[cfg(debug_assertions)]
60pub(crate) struct BorrowTracker {
61    /// Pointer addresses accessed in current phase.
62    /// Uses `UnsafeCell` for interior mutability because `Param::fetch` /
63    /// `World::track_borrow` operate on `&World`. Single-threaded,
64    /// non-reentrant access only.
65    accessed: UnsafeCell<Vec<NonNull<u8>>>,
66}
67
68#[cfg(debug_assertions)]
69impl BorrowTracker {
70    fn new() -> Self {
71        Self {
72            accessed: UnsafeCell::new(Vec::new()),
73        }
74    }
75
76    /// Reset all tracking state. Called before each `Param::fetch` phase.
77    fn clear(&self) {
78        // SAFETY: single-threaded, non-reentrant. No other reference to
79        // the inner Vec exists during this call.
80        let ptrs = unsafe { &mut *self.accessed.get() };
81        ptrs.clear();
82    }
83
84    /// Record an access. Panics if already accessed in this phase.
85    fn track(&self, id: ResourceId) {
86        // SAFETY: single-threaded, non-reentrant. No other reference to
87        // the inner Vec exists during this call.
88        let ptrs = unsafe { &mut *self.accessed.get() };
89        assert!(
90            !ptrs.contains(&id.0),
91            "conflicting access: resource {id} was accessed by more than one parameter \
92             in the same dispatch phase",
93        );
94        ptrs.push(id.0);
95    }
96}
97
98// =============================================================================
99// Core types
100// =============================================================================
101
102/// Direct pointer identifying a resource within a [`World`] container.
103///
104/// Points to a heap-allocated `ResourceCell<T>`. Dispatch-time access is
105/// a single deref — no index lookup, no Vec indirection.
106///
107/// Obtained from [`WorldBuilder::register`], [`WorldBuilder::ensure`],
108/// [`Registry::id`], [`World::id`], or their `try_` / `_default` variants.
109#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
110pub struct ResourceId(NonNull<u8>);
111
112impl ResourceId {
113    fn as_ptr(self) -> *mut u8 {
114        self.0.as_ptr()
115    }
116}
117
118// SAFETY: `ResourceId` is a thin, copyable handle to a `ResourceCell<T>`
119// allocated and pinned for the lifetime of its `World`:
120// - every `ResourceCell<T>` is registered with `T: Send`, so the pointee is
121//   safe to send between threads,
122// - the underlying pointer is stable for the `World`'s entire lifetime, and
123// - a `ResourceId` cannot be dereferenced without going through `World`,
124//   which enforces the single-threaded dispatch / aliasing invariants.
125unsafe impl Send for ResourceId {}
126
127impl std::fmt::Display for ResourceId {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        write!(f, "{:p}", self.0)
130    }
131}
132
133/// Monotonic event sequence number for event ordering.
134///
135/// Each event processed by a driver is assigned a unique sequence number
136/// via [`World::next_sequence`]. Handlers can read the current sequence
137/// via [`Seq`](crate::Seq) or advance it via [`SeqMut`](crate::SeqMut).
138///
139/// Uses `i64` for wire-format compatibility (FIX/SBE, Protobuf, Avro
140/// all have native signed 64-bit; unsigned is awkward or absent) and to
141/// support sentinel values ([`NULL`](Self::NULL),
142/// [`UNINITIALIZED`](Self::UNINITIALIZED)) without `Option` overhead.
143///
144/// Wrapping is harmless — at one increment per event, the positive `i64`
145/// space takes ~292 years at 1 GHz to exhaust.
146///
147/// # Sentinels
148///
149/// | Constant | Value | Meaning |
150/// |----------|-------|---------|
151/// | [`NULL`](Self::NULL) | `i64::MIN` | No sequence exists (SBE null convention) |
152/// | [`UNINITIALIZED`](Self::UNINITIALIZED) | `-1` | Not yet assigned |
153/// | [`ZERO`](Self::ZERO) | `0` | Starting point before any events |
154///
155/// # Examples
156///
157/// ```
158/// use nexus_rt::Sequence;
159///
160/// let a = Sequence::ZERO;
161/// let b = a.next();
162///
163/// assert!(b > a);
164/// assert_eq!(b.as_i64(), 1);
165/// assert_eq!(b.elapsed_since(a), 1);
166///
167/// assert!(Sequence::NULL.is_null());
168/// assert!(!Sequence::ZERO.is_null());
169/// ```
170#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
171pub struct Sequence(pub(crate) i64);
172
173impl Sequence {
174    /// SBE-compatible null — `i64::MIN`. Indicates no sequence exists.
175    ///
176    /// Maps directly to the SBE int64 null sentinel on the wire.
177    pub const NULL: Self = Self(i64::MIN);
178
179    /// Uninitialized sentinel — `-1`. Indicates a sequence has not yet
180    /// been assigned.
181    pub const UNINITIALIZED: Self = Self(-1);
182
183    /// The zero sequence — the starting point before any events.
184    pub const ZERO: Self = Self(0);
185
186    /// Create a sequence from a raw `i64` value.
187    ///
188    /// Use for construction in tests, deserialization, or replay.
189    pub const fn new(value: i64) -> Self {
190        Self(value)
191    }
192
193    /// Create a sequence from a raw `i64` value.
194    ///
195    /// Symmetric with [`as_i64`](Self::as_i64). Use for wire protocol
196    /// deserialization.
197    pub const fn from_i64(value: i64) -> Self {
198        Self(value)
199    }
200
201    /// Returns the raw `i64` value.
202    ///
203    /// Use for logging, metrics, serialization, or passing to external
204    /// systems.
205    pub const fn as_i64(self) -> i64 {
206        self.0
207    }
208
209    /// Returns `true` if this is the [`NULL`](Self::NULL) sentinel.
210    pub const fn is_null(self) -> bool {
211        self.0 == i64::MIN
212    }
213
214    /// Returns `true` if this is the [`UNINITIALIZED`](Self::UNINITIALIZED) sentinel.
215    pub const fn is_uninitialized(self) -> bool {
216        self.0 == -1
217    }
218
219    /// Returns the next sequence number (wrapping).
220    ///
221    /// This is a pure computation — it does not advance any world state.
222    /// Use [`World::next_sequence`] to actually advance the world's
223    /// current sequence.
224    pub const fn next(self) -> Self {
225        Self(self.0.wrapping_add(1))
226    }
227
228    /// Returns the number of events between `earlier` and `self`.
229    ///
230    /// Wrapping-aware: if `self` has wrapped past `earlier`, the result
231    /// is the wrapping distance. Returns 0 if `self == earlier`.
232    pub const fn elapsed_since(self, earlier: Self) -> i64 {
233        self.0.wrapping_sub(earlier.0)
234    }
235}
236
237impl std::fmt::Display for Sequence {
238    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239        self.0.fmt(f)
240    }
241}
242
243/// Heap-allocated wrapper for a single resource value.
244///
245/// Resources are individually heap-allocated via `Box::new(ResourceCell { value })`.
246/// The `ResourceId` is the raw pointer to this cell — a single deref to reach
247/// the value with zero framework overhead.
248pub(crate) struct ResourceCell<T> {
249    pub(crate) value: T,
250}
251
252/// Reconstruct and drop a `Box<ResourceCell<T>>` from a raw pointer.
253///
254/// # Safety
255///
256/// `ptr` must have been produced by `Box::into_raw(Box::new(ResourceCell { .. }))`
257/// where the value field is `T`. Must only be called once per pointer.
258unsafe fn drop_resource<T>(ptr: *mut u8) {
259    // SAFETY: ptr was produced by Box::into_raw(Box::new(ResourceCell { .. }))
260    // where value: T. Called exactly once in Storage::drop.
261    unsafe {
262        let _ = Box::from_raw(ptr as *mut ResourceCell<T>);
263    }
264}
265
266// =============================================================================
267// Registry — type-to-pointer mapping
268// =============================================================================
269
270/// Type-to-pointer mapping shared between [`WorldBuilder`] and [`World`].
271///
272/// Contains only the type registry — no storage backing. Passed to
273/// [`IntoHandler::into_handler`](crate::IntoHandler::into_handler) and
274/// [`Param::init`](crate::Param::init) so handlers can resolve
275/// [`ResourceId`]s during driver setup.
276///
277/// Obtained via [`WorldBuilder::registry()`] or [`World::registry()`].
278pub struct Registry {
279    indices: FxHashMap<TypeId, ResourceId>,
280}
281
282impl Registry {
283    pub(crate) fn new() -> Self {
284        Self {
285            indices: FxHashMap::default(),
286        }
287    }
288
289    /// Resolve the [`ResourceId`] for a type. Cold path — uses HashMap lookup.
290    ///
291    /// # Panics
292    ///
293    /// Panics if the resource type was not registered.
294    pub fn id<T: Resource>(&self) -> ResourceId {
295        *self
296            .indices
297            .get(&TypeId::of::<T>())
298            .unwrap_or_else(|| {
299                panic!(
300                    "resource `{}` not registered — call WorldBuilder::register::<{}>(initial_value) during setup",
301                    type_name::<T>(),
302                    type_name::<T>()
303                )
304            })
305    }
306
307    /// Try to resolve the [`ResourceId`] for a type. Returns `None` if the
308    /// type was not registered.
309    pub fn try_id<T: Resource>(&self) -> Option<ResourceId> {
310        self.indices.get(&TypeId::of::<T>()).copied()
311    }
312
313    /// Returns `true` if a resource of type `T` has been registered.
314    pub fn contains<T: Resource>(&self) -> bool {
315        self.indices.contains_key(&TypeId::of::<T>())
316    }
317
318    /// Returns the number of registered resources.
319    pub fn len(&self) -> usize {
320        self.indices.len()
321    }
322
323    /// Returns `true` if no resources have been registered.
324    pub fn is_empty(&self) -> bool {
325        self.indices.is_empty()
326    }
327
328    /// Validate that a set of parameter accesses don't conflict.
329    ///
330    /// Two accesses conflict when they target the same ResourceId (same
331    /// pointer). O(n²) pairwise comparison — handler arity is 1-8, so
332    /// this is trivially fast at build time.
333    ///
334    /// # Panics
335    ///
336    /// Panics if any resource is accessed by more than one parameter.
337    #[cold]
338    pub fn check_access(&self, accesses: &[(Option<ResourceId>, &str)]) {
339        for i in 0..accesses.len() {
340            let Some(id_i) = accesses[i].0 else { continue };
341            for j in (i + 1)..accesses.len() {
342                let Some(id_j) = accesses[j].0 else { continue };
343                assert!(
344                    id_i != id_j,
345                    "conflicting access: resource borrowed by `{}` conflicts with \
346                     resource borrowed by `{}` in the same handler",
347                    accesses[j].1,
348                    accesses[i].1,
349                );
350            }
351        }
352    }
353}
354
355// =============================================================================
356// Storage — shared backing between builder and frozen container
357// =============================================================================
358
359/// Pointer + type-erased drop function for a single resource.
360struct DropEntry {
361    ptr: *mut u8,
362    drop_fn: unsafe fn(*mut u8),
363}
364
365/// Internal storage for type-erased resource cleanup.
366///
367/// Only walked during [`World::drop`] — no dispatch-time role. The actual
368/// resource data lives in individually heap-allocated `ResourceCell<T>`
369/// values, pointed to by [`ResourceId`].
370pub(crate) struct Storage {
371    drop_entries: Vec<DropEntry>,
372}
373
374impl Storage {
375    pub(crate) fn new() -> Self {
376        Self {
377            drop_entries: Vec::new(),
378        }
379    }
380
381    pub(crate) fn len(&self) -> usize {
382        self.drop_entries.len()
383    }
384
385    pub(crate) fn is_empty(&self) -> bool {
386        self.drop_entries.is_empty()
387    }
388}
389
390// SAFETY: All values stored in Storage were registered via `register<T: Send + 'static>`,
391// so every concrete type behind the raw pointers is Send. Storage exclusively owns
392// these heap allocations — they are not aliased or shared. Transferring ownership
393// to another thread is safe.
394#[allow(clippy::non_send_fields_in_send_ty)]
395unsafe impl Send for Storage {}
396
397impl Drop for Storage {
398    fn drop(&mut self) {
399        for entry in &self.drop_entries {
400            // SAFETY: each DropEntry was created in WorldBuilder::register().
401            // drop_fn is the monomorphized destructor for the concrete
402            // ResourceCell<T> behind ptr. Called exactly once here.
403            unsafe {
404                (entry.drop_fn)(entry.ptr);
405            }
406        }
407    }
408}
409
410// =============================================================================
411// WorldBuilder
412// =============================================================================
413
414/// Builder for registering resources before freezing into a [`World`] container.
415///
416/// Each resource type can only be registered once. Registration assigns a
417/// direct [`ResourceId`] pointer.
418///
419/// The [`registry()`](Self::registry) method exposes the type-to-pointer mapping
420/// so that drivers can resolve handlers against the builder before `build()`.
421///
422/// # Examples
423///
424/// ```
425/// use nexus_rt::{WorldBuilder, Resource};
426///
427/// #[derive(Resource)]
428/// struct Counter(u64);
429/// #[derive(Resource)]
430/// struct Flag(bool);
431///
432/// let mut builder = WorldBuilder::new();
433/// let id = builder.register(Counter(42));
434/// builder.register(Flag(true));
435/// let world = builder.build();
436///
437/// unsafe {
438///     assert_eq!(world.get::<Counter>(id).0, 42);
439/// }
440/// ```
441pub struct WorldBuilder {
442    registry: Registry,
443    storage: Storage,
444}
445
446/// Marker trait for types that can be stored in a [`World`].
447///
448/// Requires `Send + 'static`. Use `#[derive(Resource)]` to implement,
449/// or [`new_resource!`](crate::new_resource) for newtype wrappers.
450///
451/// ```
452/// use nexus_rt::Resource;
453///
454/// #[derive(Resource)]
455/// struct OrderBook {
456///     bids: Vec<(f64, f64)>,
457///     asks: Vec<(f64, f64)>,
458/// }
459/// ```
460///
461/// # Why not just `Send + 'static`?
462///
463/// Without the marker trait, two modules can independently register
464/// `u64` and silently collide. The `Resource` bound forces a newtype,
465/// making collisions a compile error.
466#[diagnostic::on_unimplemented(
467    message = "this type cannot be stored as a resource in the World",
468    note = "add `#[derive(Resource)]` to your type, or use `new_resource!` for a newtype wrapper"
469)]
470pub trait Resource: Send + 'static {}
471
472// Test-only impls for primitive types used in unit tests within this crate.
473// NOT available to external crates — they should use #[derive(Resource)] or new_resource!.
474#[cfg(test)]
475mod resource_test_impls {
476    use super::Resource;
477    impl Resource for bool {}
478    impl Resource for u32 {}
479    impl Resource for u64 {}
480    impl Resource for i64 {}
481    impl Resource for f64 {}
482    impl Resource for String {}
483    impl<T: Send + 'static> Resource for Vec<T> {}
484    impl<T: Send + Sync + 'static> Resource for std::sync::Arc<T> {}
485}
486
487impl WorldBuilder {
488    /// Create an empty builder.
489    pub fn new() -> Self {
490        Self {
491            registry: Registry::new(),
492            storage: Storage::new(),
493        }
494    }
495
496    /// Register a resource and return its [`ResourceId`].
497    ///
498    /// The value is heap-allocated inside a `ResourceCell<T>` and ownership
499    /// is transferred to the container. The pointer is stable for the
500    /// lifetime of the resulting [`World`].
501    ///
502    /// # Panics
503    ///
504    /// Panics if a resource of the same type is already registered.
505    #[cold]
506    pub fn register<T: Resource>(&mut self, value: T) -> ResourceId {
507        let type_id = TypeId::of::<T>();
508        assert!(
509            !self.registry.indices.contains_key(&type_id),
510            "resource `{}` already registered",
511            type_name::<T>(),
512        );
513
514        let cell = Box::new(ResourceCell { value });
515        let raw = Box::into_raw(cell) as *mut u8;
516        // SAFETY: Box::into_raw never returns null.
517        let ptr = unsafe { NonNull::new_unchecked(raw) };
518        let id = ResourceId(ptr);
519        self.registry.indices.insert(type_id, id);
520        self.storage.drop_entries.push(DropEntry {
521            ptr: raw,
522            drop_fn: drop_resource::<T>,
523        });
524        id
525    }
526
527    /// Register a resource using its [`Default`] value and return its
528    /// [`ResourceId`].
529    ///
530    /// Equivalent to `self.register::<T>(T::default())`.
531    #[cold]
532    pub fn register_default<T: Default + Resource>(&mut self) -> ResourceId {
533        self.register(T::default())
534    }
535
536    /// Ensure a resource is registered, returning its [`ResourceId`].
537    ///
538    /// If the type is already registered, returns the existing ID and
539    /// drops `value`. If not, registers it and returns the new ID.
540    ///
541    /// Use [`register`](Self::register) when duplicate registration is a
542    /// bug that should panic. Use `ensure` when multiple plugins or
543    /// drivers may independently need the same resource type.
544    #[cold]
545    pub fn ensure<T: Resource>(&mut self, value: T) -> ResourceId {
546        if let Some(id) = self.registry.try_id::<T>() {
547            return id;
548        }
549        self.register(value)
550    }
551
552    /// Ensure a resource is registered using its [`Default`] value,
553    /// returning its [`ResourceId`].
554    ///
555    /// If the type is already registered, returns the existing ID.
556    /// If not, registers `T::default()` and returns the new ID.
557    #[cold]
558    pub fn ensure_default<T: Default + Resource>(&mut self) -> ResourceId {
559        if let Some(id) = self.registry.try_id::<T>() {
560            return id;
561        }
562        self.register(T::default())
563    }
564
565    /// Returns a shared reference to the type registry.
566    ///
567    /// Use this for construction-time calls like
568    /// [`into_handler`](crate::IntoHandler::into_handler),
569    /// [`into_callback`](crate::IntoCallback::into_callback), and
570    /// [`into_step`](crate::pipeline::IntoStep::into_step).
571    pub fn registry(&self) -> &Registry {
572        &self.registry
573    }
574
575    /// Returns a mutable reference to the type registry.
576    ///
577    /// Rarely needed — [`registry()`](Self::registry) suffices for
578    /// construction-time calls. Exists for direct mutation of the
579    /// registry if needed.
580    #[allow(dead_code)]
581    pub(crate) fn registry_mut(&mut self) -> &mut Registry {
582        &mut self.registry
583    }
584
585    /// Returns the number of registered resources.
586    pub fn len(&self) -> usize {
587        self.storage.len()
588    }
589
590    /// Returns `true` if no resources have been registered.
591    pub fn is_empty(&self) -> bool {
592        self.storage.is_empty()
593    }
594
595    /// Returns `true` if a resource of type `T` has been registered.
596    pub fn contains<T: Resource>(&self) -> bool {
597        self.registry.contains::<T>()
598    }
599
600    /// Install a plugin. The plugin is consumed and registers its
601    /// resources into this builder.
602    pub fn install_plugin(&mut self, plugin: impl crate::plugin::Plugin) -> &mut Self {
603        plugin.build(self);
604        self
605    }
606
607    /// Install a driver. The installer is consumed, registers its resources
608    /// into this builder, and returns a concrete poller for dispatch-time
609    /// polling.
610    pub fn install_driver<D: crate::driver::Installer>(&mut self, driver: D) -> D::Poller {
611        driver.install(self)
612    }
613
614    /// Freeze the builder into an immutable [`World`] container.
615    ///
616    /// After this call, no more resources can be registered. All
617    /// [`ResourceId`] values remain valid for the lifetime of the
618    /// returned [`World`].
619    ///
620    /// When the `reactors` feature is enabled, `ReactorNotify`,
621    /// `DeferredRemovals`, and
622    /// `SourceRegistry` are automatically registered
623    /// if not already present.
624    #[allow(unused_mut)]
625    pub fn build(mut self) -> World {
626        #[cfg(feature = "reactors")]
627        let (reactor_notify_id, reactor_removals_id) = {
628            self.ensure(crate::reactor::ReactorNotify::new(16, 64));
629            self.ensure(crate::reactor::DeferredRemovals::default());
630            self.ensure(crate::reactor::SourceRegistry::new());
631            (
632                self.registry.id::<crate::reactor::ReactorNotify>(),
633                self.registry.id::<crate::reactor::DeferredRemovals>(),
634            )
635        };
636
637        World {
638            registry: self.registry,
639            storage: self.storage,
640            current_sequence: Cell::new(Sequence(0)),
641            shutdown: Arc::new(AtomicBool::new(false)),
642            _not_sync: PhantomData,
643            #[cfg(feature = "reactors")]
644            reactor_notify_id,
645            #[cfg(feature = "reactors")]
646            reactor_removals_id,
647            #[cfg(feature = "reactors")]
648            reactor_events: Some(nexus_notify::Events::with_capacity(256)),
649            #[cfg(debug_assertions)]
650            borrow_tracker: BorrowTracker::new(),
651        }
652    }
653}
654
655impl Default for WorldBuilder {
656    fn default() -> Self {
657        Self::new()
658    }
659}
660
661// =============================================================================
662// World — frozen container
663// =============================================================================
664
665/// Frozen singleton resource storage.
666///
667/// Analogous to Bevy's `World`, but restricted to singleton resources
668/// (no entities, no components, no archetypes).
669///
670/// Created by [`WorldBuilder::build()`]. Resources are accessed via
671/// [`ResourceId`] direct pointers for O(1) dispatch-time access — a single
672/// pointer deref per fetch, zero framework overhead.
673///
674/// # Safe API
675///
676/// - [`resource`](Self::resource) / [`resource_mut`](Self::resource_mut) —
677///   cold-path access via HashMap lookup.
678///
679/// # Unsafe API (framework internals)
680///
681/// The low-level `get` / `get_mut` methods are `unsafe` — used by
682/// [`Param::fetch`](crate::Param) for zero-overhead dispatch.
683/// The caller must ensure no mutable aliasing.
684pub struct World {
685    /// Type-to-pointer mapping. Same registry used during build.
686    registry: Registry,
687    /// Type-erased pointer storage. Drop handled by `Storage`.
688    storage: Storage,
689    /// Current sequence number. `Cell` so handlers can advance it
690    /// through `&World` via [`SeqMut`](crate::SeqMut).
691    current_sequence: Cell<Sequence>,
692    /// Cooperative shutdown flag. Shared with [`ShutdownHandle`](crate::ShutdownHandle)
693    /// via `Arc`. Handlers access it through the [`Shutdown`](crate::Shutdown) Param.
694    shutdown: Arc<AtomicBool>,
695    /// World must not be shared across threads — it holds interior-mutable
696    /// `Cell<Sequence>` values accessed through `&self`. `!Sync` enforced by
697    /// `PhantomData<Cell<()>>`.
698    _not_sync: PhantomData<Cell<()>>,
699    /// Pre-resolved pointer to ReactorNotify for O(1) reactor operations.
700    #[cfg(feature = "reactors")]
701    reactor_notify_id: ResourceId,
702    /// Pre-resolved pointer to DeferredRemovals (avoids HashMap lookup per frame).
703    #[cfg(feature = "reactors")]
704    reactor_removals_id: ResourceId,
705    /// Pre-allocated events buffer for dispatch (avoids allocation per frame).
706    #[cfg(feature = "reactors")]
707    reactor_events: Option<nexus_notify::Events>,
708    /// Debug-only aliasing tracker. Detects duplicate resource access within
709    /// a single dispatch phase. Compiled out entirely in release builds.
710    #[cfg(debug_assertions)]
711    borrow_tracker: BorrowTracker,
712}
713
714impl World {
715    /// Convenience constructor — returns a new [`WorldBuilder`].
716    pub fn builder() -> WorldBuilder {
717        WorldBuilder::new()
718    }
719
720    /// Returns a shared reference to the type registry.
721    ///
722    /// Use this for read-only queries (e.g. [`id`](Registry::id),
723    /// [`contains`](Registry::contains)) and construction-time calls
724    /// like [`into_handler`](crate::IntoHandler::into_handler).
725    pub fn registry(&self) -> &Registry {
726        &self.registry
727    }
728
729    /// Returns a mutable reference to the type registry.
730    ///
731    /// Rarely needed — [`registry()`](Self::registry) suffices for
732    /// construction-time calls. Exists for direct mutation of the
733    /// registry if needed.
734    #[allow(dead_code)]
735    pub(crate) fn registry_mut(&mut self) -> &mut Registry {
736        &mut self.registry
737    }
738
739    /// Resolve the [`ResourceId`] for a type. Cold path — uses HashMap lookup.
740    ///
741    /// # Panics
742    ///
743    /// Panics if the resource type was not registered.
744    pub fn id<T: Resource>(&self) -> ResourceId {
745        self.registry.id::<T>()
746    }
747
748    /// Try to resolve the [`ResourceId`] for a type. Returns `None` if the
749    /// type was not registered.
750    pub fn try_id<T: Resource>(&self) -> Option<ResourceId> {
751        self.registry.try_id::<T>()
752    }
753
754    /// Returns the number of registered resources.
755    pub fn len(&self) -> usize {
756        self.storage.len()
757    }
758
759    /// Returns `true` if no resources are stored.
760    pub fn is_empty(&self) -> bool {
761        self.storage.is_empty()
762    }
763
764    /// Returns `true` if a resource of type `T` is stored.
765    pub fn contains<T: Resource>(&self) -> bool {
766        self.registry.contains::<T>()
767    }
768
769    // =========================================================================
770    // Safe resource access (cold path — HashMap lookup per call)
771    // =========================================================================
772
773    /// Safe shared access to a resource. Cold path — resolves via HashMap.
774    ///
775    /// Takes `&self` — multiple shared references can coexist. The borrow
776    /// checker prevents mixing with [`resource_mut`](Self::resource_mut)
777    /// (which takes `&mut self`).
778    ///
779    /// # Panics
780    ///
781    /// Panics if the resource type was not registered.
782    pub fn resource<T: Resource>(&self) -> &T {
783        let id = self.registry.id::<T>();
784        // SAFETY: id resolved from our own registry. &self prevents mutable
785        // aliases — resource_mut takes &mut self.
786        unsafe { self.get(id) }
787    }
788
789    /// Safe exclusive access to a resource. Cold path — resolves via HashMap.
790    ///
791    /// # Panics
792    ///
793    /// Panics if the resource type was not registered.
794    pub fn resource_mut<T: Resource>(&mut self) -> &mut T {
795        let id = self.registry.id::<T>();
796        // SAFETY: id resolved from our own registry. &mut self ensures
797        // exclusive access — no other references can exist.
798        unsafe { self.get_mut(id) }
799    }
800
801    // =========================================================================
802    // One-shot dispatch
803    // =========================================================================
804
805    /// Run a system once with full Param resolution.
806    ///
807    /// Intended for one-shot initialization after [`build()`](WorldBuilder::build).
808    /// Accepts both void-returning (`fn(params...)`) and bool-returning
809    /// (`fn(params...) -> bool`) functions via [`IntoSystem`](crate::IntoSystem).
810    /// The return value is always ignored — startup has no DAG to
811    /// propagate through. Named functions only (same closure limitation
812    /// as [`IntoHandler`](crate::IntoHandler)).
813    ///
814    /// Can be called multiple times for phased initialization.
815    ///
816    /// # Examples
817    ///
818    /// ```ignore
819    /// fn startup(
820    ///     mut driver: ResMut<MioDriver>,
821    ///     mut listener: ResMut<Listener>,
822    /// ) {
823    ///     // wire drivers to IO sources...
824    /// }
825    ///
826    /// let mut world = wb.build();
827    /// world.run_startup(startup);
828    /// ```
829    pub fn run_startup<F, Params, M>(&mut self, f: F)
830    where
831        F: crate::IntoSystem<Params, M>,
832    {
833        use crate::System;
834        let mut sys = f.into_system(&self.registry);
835        sys.run(self);
836    }
837
838    // =========================================================================
839    // Shutdown
840    // =========================================================================
841
842    /// Returns a [`ShutdownHandle`](crate::shutdown::ShutdownHandle)
843    /// sharing the same flag as the world's shutdown state.
844    ///
845    /// The handle is owned by the event loop and checked each iteration.
846    /// Handlers trigger shutdown via the [`Shutdown`](crate::Shutdown) Param.
847    pub fn shutdown_handle(&self) -> crate::shutdown::ShutdownHandle {
848        crate::shutdown::ShutdownHandle::new(Arc::clone(&self.shutdown))
849    }
850
851    /// Returns a reference to the shutdown flag.
852    ///
853    /// Used by the [`Shutdown`](crate::Shutdown) Param for direct access.
854    pub(crate) fn shutdown_flag(&self) -> &AtomicBool {
855        &self.shutdown
856    }
857
858    /// Run the event loop until shutdown is triggered.
859    ///
860    /// The closure receives `&mut World` and defines one iteration of
861    /// the poll loop — which drivers to poll, in what order, with what
862    /// timeout. The loop exits when a handler calls
863    /// [`Shutdown::trigger`](crate::Shutdown::trigger) or
864    /// an external signal flips the flag (see
865    /// `ShutdownHandle::enable_signals` (requires `signals` feature)).
866    ///
867    /// # Examples
868    ///
869    /// ```ignore
870    /// let mut world = wb.build();
871    /// world.run_startup(startup);
872    ///
873    /// world.run(|world| {
874    ///     let now = Instant::now();
875    ///     let timeout = timer.next_deadline(world)
876    ///         .map(|d| d.saturating_duration_since(now));
877    ///     mio.poll(world, timeout).expect("mio poll");
878    ///     timer.poll(world, now);
879    /// });
880    /// ```
881    pub fn run(&mut self, mut f: impl FnMut(&mut World)) {
882        while !self.shutdown.load(std::sync::atomic::Ordering::Relaxed) {
883            f(self);
884        }
885    }
886
887    // =========================================================================
888    // Sequence
889    // =========================================================================
890
891    /// Returns the current event sequence number.
892    pub fn current_sequence(&self) -> Sequence {
893        self.current_sequence.get()
894    }
895
896    /// Advance to the next event sequence number and return it.
897    ///
898    /// Drivers call this before dispatching each event. The returned
899    /// sequence number identifies the event being processed.
900    pub fn next_sequence(&mut self) -> Sequence {
901        let next = Sequence(self.current_sequence.get().0.wrapping_add(1));
902        self.current_sequence.set(next);
903        next
904    }
905
906    /// Returns a reference to the sequence `Cell`.
907    ///
908    /// Used by [`SeqMut`](crate::SeqMut) Param for direct access.
909    pub(crate) fn sequence_cell(&self) -> &Cell<Sequence> {
910        &self.current_sequence
911    }
912
913    /// Set the current sequence number directly.
914    ///
915    /// Use for recovery / replay — restores the sequence to a known
916    /// checkpoint so that subsequent `next_sequence` calls continue
917    /// from the right point.
918    pub fn set_sequence(&mut self, seq: Sequence) {
919        self.current_sequence.set(seq);
920    }
921
922    // =========================================================================
923    // Unsafe resource access (hot path — pre-resolved ResourceId)
924    // =========================================================================
925
926    /// Fetch a shared reference to a resource by direct pointer.
927    ///
928    /// # Safety
929    ///
930    /// - `id` must have been returned by [`WorldBuilder::register`] for
931    ///   the same builder that produced this container.
932    /// - `T` must be the same type that was registered at this `id`.
933    /// - The caller must ensure no mutable reference to this resource exists.
934    #[inline(always)]
935    pub unsafe fn get<T: 'static>(&self, id: ResourceId) -> &T {
936        // SAFETY: caller guarantees id was returned by register() on the
937        // builder that produced this container. T matches the registered type.
938        // No mutable alias exists. ResourceId points to a valid ResourceCell<T>.
939        unsafe { &(*(id.as_ptr() as *const ResourceCell<T>)).value }
940    }
941
942    /// Fetch a mutable reference to a resource by direct pointer.
943    ///
944    /// Takes `&self` — the container structure is frozen, but individual
945    /// resources have interior mutability via raw pointers. Sound because
946    /// callers (single-threaded sequential dispatch) uphold no-aliasing.
947    ///
948    /// # Safety
949    ///
950    /// - `id` must have been returned by [`WorldBuilder::register`] for
951    ///   the same builder that produced this container.
952    /// - `T` must be the same type that was registered at this `id`.
953    /// - The caller must ensure no other reference (shared or mutable) to this
954    ///   resource exists.
955    #[inline(always)]
956    #[allow(clippy::mut_from_ref)]
957    pub unsafe fn get_mut<T: 'static>(&self, id: ResourceId) -> &mut T {
958        // SAFETY: caller guarantees id was returned by register() on the
959        // builder that produced this container. T matches the registered type.
960        // No aliases exist. ResourceId points to a valid ResourceCell<T>.
961        unsafe { &mut (*(id.as_ptr() as *mut ResourceCell<T>)).value }
962    }
963
964    /// Reset borrow tracking for a new dispatch phase.
965    ///
966    /// Called before each [`Param::fetch`](crate::Param::fetch) in dispatch
967    /// macros. Only exists in debug builds.
968    #[cfg(debug_assertions)]
969    pub(crate) fn clear_borrows(&self) {
970        self.borrow_tracker.clear();
971    }
972
973    /// Record a resource access in the debug borrow tracker.
974    ///
975    /// Called by [`Param::fetch`](crate::Param::fetch) impls for each
976    /// resource parameter. Panics if the resource was already accessed
977    /// in the current phase (since the last [`clear_borrows`](Self::clear_borrows)).
978    /// Only exists in debug builds.
979    #[cfg(debug_assertions)]
980    pub(crate) fn track_borrow(&self, id: ResourceId) {
981        self.borrow_tracker.track(id);
982    }
983
984    // =========================================================================
985    // Reactor convenience methods (behind `reactors` feature)
986    // =========================================================================
987
988    /// Register a new data source for reactor subscriptions.
989    ///
990    /// Convenience for `world.resource_mut::<ReactorNotify>().register_source()`.
991    #[cfg(feature = "reactors")]
992    pub fn register_source(&mut self) -> crate::reactor::DataSource {
993        self.resource_mut::<crate::reactor::ReactorNotify>()
994            .register_source()
995    }
996
997    /// Create and register a reactor from a step function + context factory.
998    ///
999    /// The closure receives the assigned [`Token`](nexus_notify::Token) so
1000    /// the reactor can store it for wire routing or self-deregistration.
1001    /// Params are resolved from the internal registry — single pointer
1002    /// deref at dispatch time.
1003    ///
1004    /// This is the primary registration API. It handles the borrow
1005    /// juggling internally: allocates the token, resolves params from
1006    /// the registry, and inserts the reactor — all in one call.
1007    ///
1008    /// # Example
1009    ///
1010    /// ```ignore
1011    /// let src = world.register_source();
1012    /// world.spawn_reactor(
1013    ///     |id| QuotingCtx { reactor_id: id, instrument: BTC, layer: 1 },
1014    ///     quoting_step,
1015    /// ).subscribe(src);
1016    /// ```
1017    #[cfg(feature = "reactors")]
1018    pub fn spawn_reactor<C, Params, F: crate::reactor::IntoReactor<C, Params>>(
1019        &mut self,
1020        ctx_fn: impl FnOnce(nexus_notify::Token) -> C,
1021        step: F,
1022    ) -> crate::reactor::ReactorRegistration<'_> {
1023        // SAFETY: reactor_notify_id was resolved during build() from the same
1024        // WorldBuilder. ReactorNotify is heap-allocated via ResourceCell —
1025        // the pointer is stable for World's lifetime. We access it via raw
1026        // pointer to simultaneously read the registry (disjoint: registry is
1027        // an inline field, ReactorNotify is on the heap). &mut self guarantees
1028        // no other references to any World resource exist.
1029        let notify_ptr: *mut crate::reactor::ReactorNotify =
1030            unsafe { self.get_mut::<crate::reactor::ReactorNotify>(self.reactor_notify_id) };
1031        // SAFETY: notify_ptr is stable (heap-allocated ResourceCell).
1032        // No other references exist — previous &mut was not retained.
1033        let token = unsafe { &mut *notify_ptr }.create_reactor();
1034        let ctx = ctx_fn(token);
1035        let reactor = step.into_reactor(ctx, &self.registry);
1036        // SAFETY: notify_ptr is stable (heap-allocated ResourceCell).
1037        // No other references exist — ctx_fn and into_reactor don't alias.
1038        let notify = unsafe { &mut *notify_ptr };
1039        notify.insert_reactor(token, reactor)
1040    }
1041
1042    /// Register a pre-built reactor in one step.
1043    ///
1044    /// For reactors that don't need their token in the context, or for
1045    /// [`PipelineReactor`](crate::PipelineReactor) instances.
1046    #[cfg(feature = "reactors")]
1047    pub fn spawn_built_reactor(
1048        &mut self,
1049        reactor: impl crate::reactor::Reactor + 'static,
1050    ) -> crate::reactor::ReactorRegistration<'_> {
1051        // SAFETY: reactor_notify_id was resolved during build() from the
1052        // same WorldBuilder. &mut self guarantees exclusive access.
1053        let notify =
1054            unsafe { self.get_mut::<crate::reactor::ReactorNotify>(self.reactor_notify_id) };
1055        notify.register_built(reactor)
1056    }
1057
1058    /// Dispatch all woken reactors and process deferred removals.
1059    ///
1060    /// Call this post-frame after event handlers have called
1061    /// [`ReactorNotify::mark`](crate::ReactorNotify::mark).
1062    /// Returns `true` if any reactor ran.
1063    #[cfg(feature = "reactors")]
1064    pub fn dispatch_reactors(&mut self) -> bool {
1065        // SAFETY: reactor_notify_id was resolved during build() from the
1066        // same WorldBuilder. &mut self guarantees exclusive access. We hold
1067        // a raw pointer to allow scoped re-borrows across reactor dispatch.
1068        let notify_ptr: *mut crate::reactor::ReactorNotify =
1069            unsafe { self.get_mut::<crate::reactor::ReactorNotify>(self.reactor_notify_id) };
1070
1071        // Poll — scoped &mut, dropped before reactor dispatch.
1072        let mut events = self
1073            .reactor_events
1074            .take()
1075            .unwrap_or_else(|| nexus_notify::Events::with_capacity(256));
1076        {
1077            // SAFETY: notify_ptr is stable (heap-allocated ResourceCell).
1078            // Scoped — &mut is dropped before any other access.
1079            let notify = unsafe { &mut *notify_ptr };
1080            notify.poll(&mut events);
1081        }
1082        let ran = !events.is_empty();
1083
1084        // Dispatch — each reactor is moved out before run(), put back after.
1085        // &mut ReactorNotify is scoped tightly to avoid aliasing during run().
1086        for token in events.iter() {
1087            let idx = token.index();
1088            let reactor = {
1089                // SAFETY: notify_ptr is stable. Scoped — &mut is dropped
1090                // before reactor.run(self) which may re-borrow World.
1091                let notify = unsafe { &mut *notify_ptr };
1092                notify.take_reactor(idx)
1093            }; // &mut dropped here — safe to call run()
1094            if let Some(mut reactor) = reactor {
1095                reactor.run(self);
1096                // SAFETY: notify_ptr is stable. reactor.run() is complete,
1097                // so no World borrows remain. Safe to re-borrow notify.
1098                let notify = unsafe { &mut *notify_ptr };
1099                notify.put_reactor(idx, reactor);
1100            }
1101        }
1102
1103        // Deferred removals — swap the inner Vec out to avoid holding
1104        // &mut DeferredRemovals and &mut ReactorNotify simultaneously.
1105        // Zero allocation: the Vec is swapped back and reused next frame.
1106        // SAFETY: reactor_removals_id was resolved during build() from
1107        // the same WorldBuilder. No other references to this resource.
1108        let removals =
1109            unsafe { self.get_mut::<crate::reactor::DeferredRemovals>(self.reactor_removals_id) };
1110        let mut pending = removals.take();
1111        if !pending.is_empty() {
1112            // SAFETY: notify_ptr is stable. removals &mut was dropped
1113            // (pending now owns the data). No aliasing.
1114            let notify = unsafe { &mut *notify_ptr };
1115            while let Some(token) = pending.pop() {
1116                notify.remove_reactor(token);
1117            }
1118        }
1119        // Put the (now empty) Vec back for reuse.
1120        // SAFETY: same removals_id, no other references.
1121        let removals =
1122            unsafe { self.get_mut::<crate::reactor::DeferredRemovals>(self.reactor_removals_id) };
1123        removals.put(pending);
1124
1125        // Return events buffer for reuse next frame.
1126        self.reactor_events = Some(events);
1127
1128        ran
1129    }
1130
1131    /// Number of registered reactors.
1132    #[cfg(feature = "reactors")]
1133    pub fn reactor_count(&self) -> usize {
1134        self.resource::<crate::reactor::ReactorNotify>()
1135            .reactor_count()
1136    }
1137}
1138
1139// SAFETY: All resources are `T: Send` (enforced by `register`). World owns all
1140// heap-allocated data exclusively — the raw pointers are not aliased or shared.
1141// Transferring ownership to another thread is safe; the new thread becomes the
1142// sole accessor.
1143#[allow(clippy::non_send_fields_in_send_ty)]
1144unsafe impl Send for World {}
1145
1146// =============================================================================
1147// Tests
1148// =============================================================================
1149
1150#[cfg(test)]
1151mod tests {
1152    use super::*;
1153    use std::sync::{Arc, Weak};
1154
1155    struct Price {
1156        value: f64,
1157    }
1158    impl Resource for Price {}
1159
1160    struct Venue {
1161        name: &'static str,
1162    }
1163    impl Resource for Venue {}
1164
1165    struct Config {
1166        max_orders: usize,
1167    }
1168    impl Resource for Config {}
1169
1170    #[test]
1171    fn register_and_build() {
1172        let mut builder = WorldBuilder::new();
1173        builder.register::<Price>(Price { value: 100.0 });
1174        builder.register::<Venue>(Venue { name: "test" });
1175        let world = builder.build();
1176        #[cfg(not(feature = "reactors"))]
1177        assert_eq!(world.len(), 2);
1178        #[cfg(feature = "reactors")]
1179        assert_eq!(world.len(), 5); // + ReactorNotify + DeferredRemovals + SourceRegistry
1180    }
1181
1182    #[test]
1183    #[allow(clippy::float_cmp)]
1184    fn resource_ids_are_distinct() {
1185        let mut builder = WorldBuilder::new();
1186        let id0 = builder.register::<Price>(Price { value: 0.0 });
1187        let id1 = builder.register::<Venue>(Venue { name: "" });
1188        let id2 = builder.register::<Config>(Config { max_orders: 0 });
1189        assert_ne!(id0, id1);
1190        assert_ne!(id1, id2);
1191        assert_ne!(id0, id2);
1192
1193        let world = builder.build();
1194        unsafe {
1195            assert_eq!(world.get::<Price>(id0).value, 0.0);
1196            assert_eq!(world.get::<Venue>(id1).name, "");
1197            assert_eq!(world.get::<Config>(id2).max_orders, 0);
1198        }
1199    }
1200
1201    #[test]
1202    #[allow(clippy::float_cmp)]
1203    fn get_returns_registered_value() {
1204        let mut builder = WorldBuilder::new();
1205        builder.register::<Price>(Price { value: 42.5 });
1206        let world = builder.build();
1207
1208        let id = world.id::<Price>();
1209        // SAFETY: id resolved from this container, type matches, no aliasing.
1210        let price = unsafe { world.get::<Price>(id) };
1211        assert_eq!(price.value, 42.5);
1212    }
1213
1214    #[test]
1215    #[allow(clippy::float_cmp)]
1216    fn get_mut_modifies_value() {
1217        let mut builder = WorldBuilder::new();
1218        builder.register::<Price>(Price { value: 1.0 });
1219        let world = builder.build();
1220
1221        let id = world.id::<Price>();
1222        // SAFETY: id resolved from this container, type matches, no aliasing.
1223        unsafe {
1224            world.get_mut::<Price>(id).value = 99.0;
1225            assert_eq!(world.get::<Price>(id).value, 99.0);
1226        }
1227    }
1228
1229    #[test]
1230    fn try_id_returns_none_for_unregistered() {
1231        let world = WorldBuilder::new().build();
1232        assert!(world.try_id::<Price>().is_none());
1233    }
1234
1235    #[test]
1236    fn try_id_returns_some_for_registered() {
1237        let mut builder = WorldBuilder::new();
1238        builder.register::<Price>(Price { value: 0.0 });
1239        let world = builder.build();
1240
1241        assert!(world.try_id::<Price>().is_some());
1242    }
1243
1244    #[test]
1245    #[should_panic(expected = "already registered")]
1246    fn panics_on_duplicate_registration() {
1247        let mut builder = WorldBuilder::new();
1248        builder.register::<Price>(Price { value: 1.0 });
1249        builder.register::<Price>(Price { value: 2.0 });
1250    }
1251
1252    #[test]
1253    #[should_panic(expected = "not registered")]
1254    fn panics_on_unregistered_id() {
1255        let world = WorldBuilder::new().build();
1256        world.id::<Price>();
1257    }
1258
1259    #[test]
1260    fn empty_builder_builds_empty_world() {
1261        let world = WorldBuilder::new().build();
1262        // With the `reactors` feature, ReactorNotify + DeferredRemovals +
1263        // SourceRegistry are auto-registered. Without it, the world is truly empty.
1264        #[cfg(not(feature = "reactors"))]
1265        assert_eq!(world.len(), 0);
1266        #[cfg(feature = "reactors")]
1267        assert_eq!(world.len(), 3);
1268    }
1269
1270    #[test]
1271    fn drop_runs_destructors() {
1272        let arc = Arc::new(42u32);
1273        let weak: Weak<u32> = Arc::downgrade(&arc);
1274
1275        {
1276            let mut builder = WorldBuilder::new();
1277            builder.register::<Arc<u32>>(arc);
1278            let _world = builder.build();
1279            // Arc still alive — held by World
1280            assert!(weak.upgrade().is_some());
1281        }
1282        // World dropped — Arc should be deallocated
1283        assert!(weak.upgrade().is_none());
1284    }
1285
1286    #[test]
1287    fn builder_drop_cleans_up_without_build() {
1288        let arc = Arc::new(99u32);
1289        let weak: Weak<u32> = Arc::downgrade(&arc);
1290
1291        {
1292            let mut builder = WorldBuilder::new();
1293            builder.register::<Arc<u32>>(arc);
1294        }
1295        // Builder dropped without build() — Storage::drop cleans up
1296        assert!(weak.upgrade().is_none());
1297    }
1298
1299    #[test]
1300    #[allow(clippy::float_cmp)]
1301    fn multiple_types_independent() {
1302        let mut builder = WorldBuilder::new();
1303        let price_id = builder.register::<Price>(Price { value: 10.0 });
1304        let venue_id = builder.register::<Venue>(Venue { name: "CB" });
1305        let config_id = builder.register::<Config>(Config { max_orders: 500 });
1306        let world = builder.build();
1307
1308        unsafe {
1309            assert_eq!(world.get::<Price>(price_id).value, 10.0);
1310            assert_eq!(world.get::<Venue>(venue_id).name, "CB");
1311            assert_eq!(world.get::<Config>(config_id).max_orders, 500);
1312        }
1313    }
1314
1315    #[test]
1316    fn contains_reflects_registration() {
1317        let mut builder = WorldBuilder::new();
1318        assert!(!builder.contains::<Price>());
1319
1320        builder.register::<Price>(Price { value: 0.0 });
1321        assert!(builder.contains::<Price>());
1322        assert!(!builder.contains::<Venue>());
1323
1324        let world = builder.build();
1325        assert!(world.contains::<Price>());
1326        assert!(!world.contains::<Venue>());
1327    }
1328
1329    #[test]
1330    #[allow(clippy::float_cmp)]
1331    fn send_to_another_thread() {
1332        let mut builder = WorldBuilder::new();
1333        builder.register::<Price>(Price { value: 55.5 });
1334        let world = builder.build();
1335
1336        let handle = std::thread::spawn(move || {
1337            let id = world.id::<Price>();
1338            // SAFETY: sole owner on this thread, no aliasing.
1339            unsafe { world.get::<Price>(id).value }
1340        });
1341        assert_eq!(handle.join().unwrap(), 55.5);
1342    }
1343
1344    #[test]
1345    fn registry_accessible_from_builder() {
1346        let mut builder = WorldBuilder::new();
1347        let registered_id = builder.register::<u64>(42);
1348
1349        let registry = builder.registry();
1350        assert!(registry.contains::<u64>());
1351        assert!(!registry.contains::<bool>());
1352
1353        let id = registry.id::<u64>();
1354        assert_eq!(id, registered_id);
1355    }
1356
1357    #[test]
1358    fn registry_accessible_from_world() {
1359        let mut builder = WorldBuilder::new();
1360        builder.register::<u64>(42);
1361        let world = builder.build();
1362
1363        let registry = world.registry();
1364        assert!(registry.contains::<u64>());
1365
1366        // Registry from world and world.id() agree.
1367        assert_eq!(registry.id::<u64>(), world.id::<u64>());
1368    }
1369
1370    // -- Safe accessor tests --------------------------------------------------
1371
1372    #[test]
1373    #[allow(clippy::float_cmp)]
1374    fn resource_reads_value() {
1375        let mut builder = WorldBuilder::new();
1376        builder.register::<Price>(Price { value: 42.5 });
1377        let world = builder.build();
1378
1379        assert_eq!(world.resource::<Price>().value, 42.5);
1380    }
1381
1382    #[test]
1383    fn resource_mut_modifies_value() {
1384        let mut builder = WorldBuilder::new();
1385        builder.register::<u64>(0);
1386        let mut world = builder.build();
1387
1388        *world.resource_mut::<u64>() = 99;
1389        assert_eq!(*world.resource::<u64>(), 99);
1390    }
1391
1392    #[test]
1393    fn register_default_works() {
1394        let mut builder = WorldBuilder::new();
1395        let id = builder.register_default::<Vec<u32>>();
1396        let world = builder.build();
1397
1398        assert_eq!(id, world.id::<Vec<u32>>());
1399        let v = world.resource::<Vec<u32>>();
1400        assert!(v.is_empty());
1401    }
1402
1403    #[test]
1404    fn ensure_registers_new_type() {
1405        let mut builder = WorldBuilder::new();
1406        let id = builder.ensure::<u64>(42);
1407        let world = builder.build();
1408
1409        assert_eq!(id, world.id::<u64>());
1410        assert_eq!(*world.resource::<u64>(), 42);
1411    }
1412
1413    #[test]
1414    fn ensure_returns_existing_id() {
1415        let mut builder = WorldBuilder::new();
1416        let id1 = builder.register::<u64>(42);
1417        let id2 = builder.ensure::<u64>(99);
1418        assert_eq!(id1, id2);
1419
1420        // Original value preserved, new value dropped.
1421        let world = builder.build();
1422        assert_eq!(*world.resource::<u64>(), 42);
1423    }
1424
1425    #[test]
1426    fn ensure_default_registers_new_type() {
1427        let mut builder = WorldBuilder::new();
1428        let id = builder.ensure_default::<Vec<u32>>();
1429        let world = builder.build();
1430
1431        assert_eq!(id, world.id::<Vec<u32>>());
1432        assert!(world.resource::<Vec<u32>>().is_empty());
1433    }
1434
1435    #[test]
1436    fn ensure_default_returns_existing_id() {
1437        let mut builder = WorldBuilder::new();
1438        builder.register::<Vec<u32>>(vec![1, 2, 3]);
1439        let id = builder.ensure_default::<Vec<u32>>();
1440        let world = builder.build();
1441
1442        assert_eq!(id, world.id::<Vec<u32>>());
1443        // Original value preserved.
1444        assert_eq!(*world.resource::<Vec<u32>>(), vec![1, 2, 3]);
1445    }
1446
1447    // -- Sequence tests -----------------------------------------------------------
1448
1449    #[test]
1450    fn sequence_default_is_zero() {
1451        assert_eq!(Sequence::default(), Sequence(0));
1452    }
1453
1454    #[test]
1455    fn next_sequence_increments() {
1456        let mut world = WorldBuilder::new().build();
1457        assert_eq!(world.current_sequence(), Sequence(0));
1458        world.next_sequence();
1459        assert_eq!(world.current_sequence(), Sequence(1));
1460        world.next_sequence();
1461        assert_eq!(world.current_sequence(), Sequence(2));
1462    }
1463
1464    // -- run_startup tests ----------------------------------------------------
1465
1466    #[test]
1467    fn run_startup_dispatches_handler() {
1468        use crate::ResMut;
1469
1470        let mut builder = WorldBuilder::new();
1471        builder.register::<u64>(0);
1472        builder.register::<bool>(false);
1473        let mut world = builder.build();
1474
1475        fn init(mut counter: ResMut<u64>, mut flag: ResMut<bool>) {
1476            *counter = 42;
1477            *flag = true;
1478        }
1479
1480        world.run_startup(init);
1481
1482        assert_eq!(*world.resource::<u64>(), 42);
1483        assert!(*world.resource::<bool>());
1484    }
1485
1486    #[test]
1487    fn run_startup_multiple_phases() {
1488        use crate::ResMut;
1489
1490        let mut builder = WorldBuilder::new();
1491        builder.register::<u64>(0);
1492        let mut world = builder.build();
1493
1494        fn phase1(mut counter: ResMut<u64>) {
1495            *counter += 10;
1496        }
1497
1498        fn phase2(mut counter: ResMut<u64>) {
1499            *counter += 5;
1500        }
1501
1502        world.run_startup(phase1);
1503        world.run_startup(phase2);
1504
1505        assert_eq!(*world.resource::<u64>(), 15);
1506    }
1507
1508    // -- Plugin / Driver tests ------------------------------------------------
1509
1510    #[test]
1511    fn plugin_registers_resources() {
1512        struct TestPlugin;
1513
1514        impl crate::plugin::Plugin for TestPlugin {
1515            fn build(self, world: &mut WorldBuilder) {
1516                world.register::<u64>(42);
1517                world.register::<bool>(true);
1518            }
1519        }
1520
1521        let mut builder = WorldBuilder::new();
1522        builder.install_plugin(TestPlugin);
1523        let world = builder.build();
1524
1525        assert_eq!(*world.resource::<u64>(), 42);
1526        assert!(*world.resource::<bool>());
1527    }
1528
1529    #[test]
1530    fn driver_installs_and_returns_handle() {
1531        struct TestInstaller;
1532        struct TestHandle {
1533            counter_id: ResourceId,
1534        }
1535
1536        impl crate::driver::Installer for TestInstaller {
1537            type Poller = TestHandle;
1538
1539            fn install(self, world: &mut WorldBuilder) -> TestHandle {
1540                let counter_id = world.register::<u64>(0);
1541                TestHandle { counter_id }
1542            }
1543        }
1544
1545        let mut builder = WorldBuilder::new();
1546        let handle = builder.install_driver(TestInstaller);
1547        let world = builder.build();
1548
1549        // Handle's pre-resolved ID can access the resource.
1550        unsafe {
1551            assert_eq!(*world.get::<u64>(handle.counter_id), 0);
1552        }
1553    }
1554
1555    // -- check_access conflict detection ----------------------------------------
1556
1557    #[test]
1558    fn check_access_no_conflict() {
1559        let mut builder = WorldBuilder::new();
1560        let id_a = builder.register::<u64>(0);
1561        let id_b = builder.register::<u32>(0);
1562        builder
1563            .registry()
1564            .check_access(&[(Some(id_a), "a"), (Some(id_b), "b")]);
1565    }
1566
1567    #[test]
1568    #[should_panic(expected = "conflicting access")]
1569    fn check_access_detects_conflict() {
1570        let mut builder = WorldBuilder::new();
1571        let id = builder.register::<u64>(0);
1572        builder
1573            .registry()
1574            .check_access(&[(Some(id), "a"), (Some(id), "b")]);
1575    }
1576
1577    #[test]
1578    fn sequence_wrapping() {
1579        let builder = WorldBuilder::new();
1580        let mut world = builder.build();
1581
1582        // Advance to MAX.
1583        world.current_sequence.set(Sequence(i64::MAX));
1584        assert_eq!(world.current_sequence(), Sequence(i64::MAX));
1585
1586        // Wrap to MIN (which is the NULL sentinel, but wrapping is
1587        // purely mechanical — it doesn't assign semantic meaning).
1588        let seq = world.next_sequence();
1589        assert_eq!(seq, Sequence(i64::MIN));
1590        assert_eq!(world.current_sequence(), Sequence(i64::MIN));
1591    }
1592
1593    // -- BorrowTracker tests (debug builds only) ------------------------------
1594
1595    #[cfg(debug_assertions)]
1596    #[test]
1597    #[should_panic(expected = "conflicting access")]
1598    fn borrow_tracker_catches_double_access() {
1599        let mut builder = WorldBuilder::new();
1600        let id = builder.register::<u64>(42);
1601        let world = builder.build();
1602        world.clear_borrows();
1603        world.track_borrow(id);
1604        world.track_borrow(id); // same resource, same phase
1605    }
1606
1607    #[cfg(debug_assertions)]
1608    #[test]
1609    fn borrow_tracker_allows_after_clear() {
1610        let mut builder = WorldBuilder::new();
1611        let id = builder.register::<u64>(42);
1612        let world = builder.build();
1613        world.clear_borrows();
1614        world.track_borrow(id);
1615        world.clear_borrows();
1616        world.track_borrow(id); // new phase, no conflict
1617    }
1618
1619    #[cfg(debug_assertions)]
1620    #[test]
1621    fn borrow_tracker_different_resources_ok() {
1622        let mut builder = WorldBuilder::new();
1623        let id_a = builder.register::<u64>(1);
1624        let id_b = builder.register::<u32>(2);
1625        let world = builder.build();
1626        world.clear_borrows();
1627        world.track_borrow(id_a);
1628        world.track_borrow(id_b); // different resources, no conflict
1629    }
1630}