1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
//! Support for routing effects to explicit, type-based handlers.
//!
//! This module enables an advanced use-case, where some effects are not
//! handled by the shell using the standard serialization-based FFI interface.
//! Instead the core FFI can be extended with core-side effect processing or
//! custom effect handling FFI APIs, handling the operations and outputs using
//! a different data exchange method (e.g. raw pointers, zero-copy formats like
//! Cap'n Proto, etc.)
//!
//! # Overview
//!
//! The entry point is the [`EffectRouter`], which wraps a [`Core`] and a
//! routing closure. The closure inspects each [`Effect`](crate::App::Effect)
//! the app emits and dispatches it to the appropriate handler, or "lane". Crucially,
//! the follow-up effects produced when a request is resolved are routed back
//! through the same closure, so the same policy applies for the whole lifetime
//! of a chain of effects.
//!
//! The available lanes live in the [`routes`] module:
//!
//! - [`Serialized`](routes::Serialized) keeps the standard, bridge-like
//! behaviour: effects are serialized to bytes, sent to the shell, and
//! resolved by id with serialized responses. This is the default lane and the
//! primary onboarding path; it typically acts as the fall-through arm of the
//! routing closure.
//! - [`Parked`](routes::Parked) supports payloads and results that are awkward
//! or undesirable to serialize (for example opaque pointer-style handles),
//! using a custom, user-owned FFI. The request is parked under an
//! [`EffectId`] which the shell passes back when resolving.
//! - [`Buffer`](routes::Buffer) collects requests for the caller to drain and
//! handle synchronously, which is useful in tests and simple in-process
//! handlers.
//!
//! Effects can also be handled entirely inside the core by a Rust handler
//! (including async or background work) that resolves requests back through the
//! router via the [`ResolveSink`] trait.
//!
//! # Wiring it up
//!
//! Routes are grouped in a user-defined type that implements [`Routes`]. The
//! router is created with [`EffectRouter::new`], which hands the constructed
//! route set to a builder closure so it can be captured by the routing closure.
//! Because routes need to resolve effects back through the router, they hold a
//! [`Weak`] reference to it, and the router is therefore stored behind an
//! [`Arc`].
//!
//! See the `effect_router_prototype` integration test in `crux_core` for a
//! complete, worked example, and `docs/src/rfcs/effect-router.md` for the
//! design rationale.
use ;
use crate::;
pub use EffectId;
/// Wraps a [`Core`] and routes each emitted effect to a type-specific handler.
///
/// The router owns the set of routes ([`RouteSet`](Routes)) and a routing closure which
/// decides, per effect, which handler should process it. Any follow-up effects
/// produced while resolving a request are passed back through the same closure,
/// so routing decisions stay consistent across an entire chain of effects.
///
/// Construct one with [`EffectRouter::new`]. The router is always held behind an
/// [`Arc`] so that individual routes can keep a [`Weak`] reference back to it
/// and drive the runtime forward when they resolve requests.
/// A set of effect handlers ("routes") owned by an [`EffectRouter`].
///
/// Implement this on a type that groups together the individual routes your app
/// needs (for example a [`Serialized`](routes::Serialized) lane plus one or more
/// [`Parked`](routes::Parked) lanes and core-local handlers). The router calls
/// [`Routes::new`] while it is being constructed, handing over a [`Weak`]
/// reference to itself so each route can later resolve requests and advance the
/// runtime.
///
/// The type must be [`Clone`] because a clone is given to the routing closure
/// built in [`EffectRouter::new`]; routes are typically wrapped in [`Arc`] so
/// cloning is cheap and shares the same underlying handlers.
/// Lets a core-local handler resolve a [`Request`] back through the router.
///
/// Core-side handlers (for example background workers that do async I/O) hold a
/// [`Weak`] reference to something implementing this trait, typically the
/// [`EffectRouter`] itself. When a handler finishes its work, it calls
/// [`ResolveSink::resolve_request`] to resolve the original request and advance
/// the runtime, so any follow-up effects are routed using the same policy.
///
/// The trait is generic over the [`Operation`] `Op` so a handler only depends on
/// the single operation type it knows how to service, rather than the whole
/// route set.