Skip to main content

optional_resources/
optional_resources.rs

1//! Optional resource dependencies with `Option<Res<T>>` / `Option<ResMut<T>>`.
2//!
3//! Handlers that use `Option<Res<T>>` or `Option<ResMut<T>>` will run
4//! regardless of whether `T` is registered in World:
5//!
6//! - **Registered:** the handler receives `Some(Res<T>)` / `Some(ResMut<T>)`
7//! - **Not registered:** the handler receives `None`
8//!
9//! This is resolved at build time via `registry.try_id::<T>()`. There is
10//! no runtime overhead — the Option<ResourceId> state is cached once.
11//!
12//! Use cases:
13//! - Feature flags (enable debug logging only if DebugConfig is registered)
14//! - Graceful degradation (metrics sink may or may not exist)
15//! - Plugin systems (optional extensions that aren't always present)
16//!
17//! Run with:
18//! ```bash
19//! cargo run -p nexus-rt --example optional_resources
20//! ```
21
22use nexus_rt::{Handler, IntoHandler, Res, ResMut, WorldBuilder};
23
24// -- Domain types ------------------------------------------------------------
25
26struct Config {
27    threshold: f64,
28}
29
30/// Optional — only present if debug mode is enabled.
31struct DebugLog {
32    entries: Vec<String>,
33}
34
35/// Optional — only present if metrics are enabled.
36struct Metrics {
37    events_processed: u64,
38}
39
40// -- Handlers ----------------------------------------------------------------
41
42/// Always runs. Uses Config (required) and DebugLog (optional).
43fn process_event(config: Res<Config>, mut debug: Option<ResMut<DebugLog>>, value: f64) {
44    let above = value > config.threshold;
45    println!(
46        "[process] value={:.1}, threshold={:.1}, above={}",
47        value, config.threshold, above
48    );
49
50    if let Some(ref mut log) = debug {
51        log.entries
52            .push(format!("processed {:.1} (above={})", value, above));
53        println!("  -> logged to debug ({} entries)", log.entries.len());
54    }
55}
56
57/// Always runs. Increments metrics if they exist.
58fn track_metrics(metrics: Option<ResMut<Metrics>>, _event: f64) {
59    match metrics {
60        Some(mut m) => {
61            m.events_processed += 1;
62            println!("[metrics] events_processed = {}", m.events_processed);
63        }
64        None => {
65            println!("[metrics] no metrics sink registered — skipping");
66        }
67    }
68}
69
70fn main() {
71    println!("=== Scenario 1: All optional resources present ===\n");
72    {
73        let mut builder = WorldBuilder::new();
74        builder
75            .register(Config { threshold: 5.0 })
76            .register(DebugLog {
77                entries: Vec::new(),
78            })
79            .register(Metrics {
80                events_processed: 0,
81            });
82        let mut world = builder.build();
83
84        let mut process = process_event.into_handler(world.registry_mut());
85        let mut track = track_metrics.into_handler(world.registry_mut());
86
87        for value in [3.0, 7.5, 1.2] {
88            process.run(&mut world, value);
89            track.run(&mut world, value);
90            println!();
91        }
92
93        let log = world.resource::<DebugLog>();
94        println!("Debug log entries: {:?}", log.entries);
95
96        let metrics = world.resource::<Metrics>();
97        println!("Events processed: {}", metrics.events_processed);
98    }
99
100    println!("\n=== Scenario 2: No optional resources ===\n");
101    {
102        let mut builder = WorldBuilder::new();
103        builder.register(Config { threshold: 5.0 });
104        // DebugLog and Metrics intentionally not registered.
105        let mut world = builder.build();
106
107        let mut process = process_event.into_handler(world.registry_mut());
108        let mut track = track_metrics.into_handler(world.registry_mut());
109
110        for value in [3.0, 7.5] {
111            process.run(&mut world, value);
112            track.run(&mut world, value);
113            println!();
114        }
115
116        println!("Handlers ran cleanly without optional resources.");
117    }
118
119    println!("\nDone.");
120}