moonpool_sim/runner/context.rs
1//! Simulation context for workloads.
2//!
3//! [`SimContext`] is the single entry point for workloads to access simulation
4//! infrastructure: providers, topology, shared state, and shutdown signaling.
5//!
6//! # Usage
7//!
8//! ```ignore
9//! use moonpool_sim::SimContext;
10//!
11//! async fn my_workload(ctx: &SimContext) -> SimulationResult<()> {
12//! let server_ip = ctx.peer("server").expect("server not found");
13//! let stream = ctx.network().connect(&server_ip).await?;
14//! ctx.state().publish("connected", true);
15//! Ok(())
16//! }
17//! ```
18
19use crate::chaos::state_handle::StateHandle;
20use crate::network::SimNetworkProvider;
21use crate::providers::{SimProviders, SimRandomProvider, SimTimeProvider};
22use crate::storage::SimStorageProvider;
23
24use moonpool_core::{Providers, TokioTaskProvider};
25
26use super::topology::WorkloadTopology;
27
28/// Simulation context provided to workloads.
29///
30/// Wraps all simulation infrastructure into a single, non-generic struct.
31/// For code generic over `P: Providers`, pass `ctx.providers()`.
32pub struct SimContext {
33 providers: SimProviders,
34 topology: WorkloadTopology,
35 state: StateHandle,
36}
37
38impl SimContext {
39 /// Create a new simulation context.
40 pub fn new(providers: SimProviders, topology: WorkloadTopology, state: StateHandle) -> Self {
41 Self {
42 providers,
43 topology,
44 state,
45 }
46 }
47
48 /// Get the full providers bundle for passing to generic code.
49 pub fn providers(&self) -> &SimProviders {
50 &self.providers
51 }
52
53 /// Get the simulated network provider.
54 pub fn network(&self) -> &SimNetworkProvider {
55 self.providers.network()
56 }
57
58 /// Get the simulated time provider.
59 pub fn time(&self) -> &SimTimeProvider {
60 self.providers.time()
61 }
62
63 /// Get the task provider.
64 pub fn task(&self) -> &TokioTaskProvider {
65 self.providers.task()
66 }
67
68 /// Get the seeded random provider.
69 pub fn random(&self) -> &SimRandomProvider {
70 self.providers.random()
71 }
72
73 /// Get the simulated storage provider.
74 pub fn storage(&self) -> &SimStorageProvider {
75 self.providers.storage()
76 }
77
78 /// Get this workload's IP address.
79 pub fn my_ip(&self) -> &str {
80 &self.topology.my_ip
81 }
82
83 /// Get this workload's client ID.
84 ///
85 /// Assigned by the builder's [`ClientId`](crate::ClientId) strategy.
86 /// Defaults to sequential IDs starting from 0 (FDB-style).
87 pub fn client_id(&self) -> usize {
88 self.topology.client_id
89 }
90
91 /// Get the total number of workload instances sharing this entry.
92 ///
93 /// For single `.workload()` entries this is 1.
94 /// For `.workloads(count, factory)` entries this is the resolved count.
95 pub fn client_count(&self) -> usize {
96 self.topology.client_count
97 }
98
99 /// Find a peer's IP address by workload name.
100 pub fn peer(&self, name: &str) -> Option<String> {
101 self.topology.get_peer_by_name(name)
102 }
103
104 /// Get all peers as (name, ip) pairs.
105 pub fn peers(&self) -> Vec<(String, String)> {
106 self.topology
107 .peer_names
108 .iter()
109 .zip(self.topology.peer_ips.iter())
110 .map(|(name, ip)| (name.clone(), ip.clone()))
111 .collect()
112 }
113
114 /// Get the shutdown cancellation token.
115 pub fn shutdown(&self) -> &tokio_util::sync::CancellationToken {
116 &self.topology.shutdown_signal
117 }
118
119 /// Get the workload topology (peer IPs, process IPs, tags, etc.).
120 pub fn topology(&self) -> &WorkloadTopology {
121 &self.topology
122 }
123
124 /// Get the shared state handle for cross-workload communication.
125 pub fn state(&self) -> &StateHandle {
126 &self.state
127 }
128}