Skip to main content

ridl_rt/
lib.rs

1//! The library that generated code links and a runtime implements.
2//!
3//! `ridl-rt` defines, once, the vocabulary that a package generated from ridl
4//! and a runtime agree on: identity, time and the envelope, samples, the
5//! payload traits, the interaction descriptors, the ports, and the contract and
6//! transport errors. It contains no runtime, but it carries the pure data
7//! structures every runtime would otherwise write alone, such as the
8//! [`correlate`] call table. A runtime is a separate crate that
9//! implements the traits of the `port` module (ADR-0020 decision 6), and
10//! generated code calls those traits without naming the runtime.
11//!
12//! With the `std` feature off — the default — the crate is `no_std` and
13//! allocates nothing; it contains no `unsafe` code and has no dependency in any
14//! feature combination. The cargo features `flatbuffers`,
15//! `proto3` and `repr-c` name the payload encodings. `flatbuffers` enables the
16//! [`flatbuffers`] module, the reading and writing a generated
17//! `Payload<FlatBuffers>` implementation shares; `proto3` and `repr-c` enable
18//! nothing in this version. A fourth feature, `std`, off by default, is not an
19//! encoding: it links the standard library and enables the [`task`] module —
20//! `block_on`, which waits on a future by parking the thread until a deadline,
21//! `noop_waker`, and `flag_waker`, whose wake sets a flag the caller reads —
22//! for a blocking client built over an async one and for a frame loop that
23//! polls a future once per frame, or again in the same frame when the future
24//! woke itself. `task` links the standard library and allocates — one `Arc` per
25//! call of any of the three functions. [`trace`] also links the standard
26//! library, for its hook: a `OnceLock` and an `Error` impl, with no allocation.
27//! Every other module stays `no_std` with the feature on.
28//!
29//! Every public item lives in one of nine modules, or in one of the two that
30//! the `flatbuffers` and `std` features add. Generated code names each
31//! item by its full path, for example `ridl_rt::sample::Sample`, and imports
32//! none, because several names here — `Duration`, `Handler`, `Kind` — are also
33//! names in `core` or in application code. The eighth module, [`face`], is the
34//! one whose items generated code implements rather than calls: the traits
35//! that carry the fixed methods of a generated `Client` and `Publisher` —
36//! `new`, `next_event`, `commit`, and under `std` `with_timeout` and
37//! `set_timeout` — so that a member of an interface may carry one of those
38//! names. A consumer of a generated face brings them into scope with
39//! `use <crate>::<iface>::prelude::*;` for each interface whose face it uses;
40//! each prelude brings the traits that interface's types implement, and
41//! rustc reports a prelude as an unused import when the other imported
42//! preludes already bring every item it would add.
43//!
44//! The ninth module, [`trace`], holds `TraceContext`, the optional trace context
45//! that a call or an event carries across a port. With the `std` feature it also
46//! holds `Propagation`, `AlreadySet`, `set_propagation` and `propagation`, the
47//! application's hook for that context.
48//!
49//! # Where to start
50//!
51//! Most of this crate is called by generated code, not by an application. An
52//! application implements one generated `Provider` trait and calls generated
53//! `Client` and `Publisher` methods; those methods call the ports here. So the
54//! shortest path in is to read one interaction kind at a time, from the
55//! generated side:
56//!
57//! | To do this                       | The generated face gives you | Over this port                                |
58//! | -------------------------------- | ---------------------------- | --------------------------------------------- |
59//! | Read a signal                    | `Client::<name>`             | [`port::SignalReader`], returning [`sample::Sample`] |
60//! | Publish a signal                 | `Publisher::<name>`, `commit` | [`port::SignalWriter`]                        |
61//! | Receive an event                 | `Client::subscribe_<name>`, `Client::next_event` | [`port::EventSource`]     |
62//! | Raise an event                   | `Publisher::<name>`          | [`port::EventSink`]                           |
63//! | Call a command or a query        | `Client::<name>`, returning the call's future | [`port::Caller`]             |
64//! | Serve a command or a query       | `Provider`, driven by the generated `serve` | [`port::Handler`]              |
65//! | Read a provisioned constant      | nothing yet — call the port  | [`port::FixedReader`]                         |
66//!
67//! `docs/technotes/ridl-rt-by-example.md` in this repository walks that table
68//! from top to bottom against concrete generated code, introducing each type
69//! at the point where the generated code first needs it. The library's own
70//! as-built description is `docs/design/ridl-rt.md`.
71//!
72//! Two properties hold everywhere and are worth knowing before reading any
73//! individual item. **No port method waits** — every one returns immediately,
74//! and a call's outcome is retrieved separately through a
75//! [`port::Correlation`]. A face that waits registers its interest with the
76//! [`port::Wakeable`] extension, keyed by a [`port::Interest`], and reads the
77//! port again when the runtime wakes it; a runtime that serves a generated
78//! async client implements that extension. And **no port names a payload
79//! type** — ports carry interface numbers, ordinals and bytes, and the
80//! generated binding is what encodes and decodes, through [`payload::Ref`].
81//!
82//! A call made through the generated async client returns a named future,
83//! and the runtime keeps the call's outcome until the caller releases it. The
84//! future releases it: it calls [`port::Caller::forget`] when it leaves the
85//! waiting phase — in the poll that takes the outcome, at the call's deadline,
86//! and on drop while the call is still waiting — so a program over the
87//! generated face holds one slot per call in flight and forgets nothing by
88//! hand. A program that sends through [`port::Caller`] directly must call
89//! [`port::Caller::forget`] on a correlation once it has read the outcome;
90//! otherwise a runtime that bounds how many calls it holds at once refuses
91//! every call with [`port::SendError::Busy`] once that bound is reached.
92//!
93//! # How a runtime presents its ports
94//!
95//! A runtime crate exposes one handle type per port role it implements — a
96//! port role is one port trait — rather than one type implementing them all,
97//! and it may also offer an aggregate handle covering the port set one
98//! interface's face needs. A generated face is built over one value
99//! implementing at least the port traits its interface needs: the role handle
100//! itself when the face needs exactly one, and an aggregate — the runtime's,
101//! or one the application writes over role handles — when it needs more,
102//! because a generated `Client` may be bound over `SignalReader`,
103//! `EventSource` and `Caller` at once. Either value reaches the face by value
104//! or as a `&mut` borrow of itself, because every port trait is implemented
105//! for `&mut P` and the `&self`-only traits also for `&P`. In a runtime whose
106//! handles are used from more than one thread, a handle whose port traits all
107//! take `&self` is `Send + Sync`, because several threads may read one store
108//! at once, and a handle carrying a trait with a `&mut self` method is `Send`
109//! and need not be `Sync`, because one thread drives each. No port trait here
110//! carries `Send` or `Sync` as a supertrait, so a single-threaded `no_std`
111//! runtime whose handles use `Cell` or `RefCell` internally is held to
112//! neither; a runtime checks its own handles itself, with a compile-time
113//! assertion. ADR-0021 decision 12 records this.
114
115#![no_std]
116#![forbid(unsafe_code)]
117
118// The `task` and `trace` modules name the standard library under the `std`
119// feature.
120#[cfg(feature = "std")]
121extern crate std;
122
123pub mod contract;
124pub mod correlate;
125pub mod encoding;
126pub mod error;
127pub mod face;
128#[cfg(feature = "flatbuffers")]
129pub mod flatbuffers;
130pub mod payload;
131pub mod port;
132pub mod sample;
133#[cfg(feature = "std")]
134pub mod task;
135pub mod trace;
136
137/// Pins which enums stay `#[non_exhaustive]` under R-11: `Transport`,
138/// `ReadError`, `WriteError`, `RaiseError`, `SendError`, `SubscribeError`,
139/// `ServeError`, `SettleError`, `ClientError` and `ProviderError`. Each
140/// `compile_fail` block below matches every variant of one such enum, with
141/// no `_` arm. Matching a
142/// `#[non_exhaustive]` enum from outside its crate with no `_` arm does not
143/// compile, so a block fails until `#[non_exhaustive]` is removed from the
144/// enum it names.
145///
146/// ```compile_fail
147/// fn f(x: ridl_rt::error::Transport) {
148///     match x {
149///         ridl_rt::error::Transport::Timeout => {}
150///         ridl_rt::error::Transport::Undelivered => {}
151///         ridl_rt::error::Transport::Down => {}
152///         ridl_rt::error::Transport::Corrupt => {}
153///         ridl_rt::error::Transport::Busy => {}
154///     }
155/// }
156/// ```
157///
158/// ```compile_fail
159/// fn f(x: ridl_rt::port::ReadError) {
160///     match x {
161///         ridl_rt::port::ReadError::Short { .. } => {}
162///         ridl_rt::port::ReadError::ShortClaim { .. } => {}
163///         ridl_rt::port::ReadError::TooFewSamples { .. } => {}
164///         ridl_rt::port::ReadError::Contract(_) => {}
165///         ridl_rt::port::ReadError::Detached => {}
166///     }
167/// }
168/// ```
169///
170/// ```compile_fail
171/// fn f(x: ridl_rt::port::WriteError) {
172///     match x {
173///         ridl_rt::port::WriteError::TooLarge { .. } => {}
174///         ridl_rt::port::WriteError::NotOwner => {}
175///         ridl_rt::port::WriteError::Contract(_) => {}
176///         ridl_rt::port::WriteError::Detached => {}
177///     }
178/// }
179/// ```
180///
181/// ```compile_fail
182/// fn f(x: ridl_rt::port::RaiseError) {
183///     match x {
184///         ridl_rt::port::RaiseError::Busy => {}
185///         ridl_rt::port::RaiseError::TooLarge { .. } => {}
186///         ridl_rt::port::RaiseError::NotOwner => {}
187///         ridl_rt::port::RaiseError::Contract(_) => {}
188///         ridl_rt::port::RaiseError::Detached => {}
189///     }
190/// }
191/// ```
192///
193/// ```compile_fail
194/// fn f(x: ridl_rt::port::SendError) {
195///     match x {
196///         ridl_rt::port::SendError::Busy => {}
197///         ridl_rt::port::SendError::TooLarge { .. } => {}
198///         ridl_rt::port::SendError::Contract(_) => {}
199///         ridl_rt::port::SendError::Detached => {}
200///     }
201/// }
202/// ```
203///
204/// ```compile_fail
205/// fn f(x: ridl_rt::port::SubscribeError) {
206///     match x {
207///         ridl_rt::port::SubscribeError::Contract(_) => {}
208///         ridl_rt::port::SubscribeError::Detached => {}
209///     }
210/// }
211/// ```
212///
213/// ```compile_fail
214/// fn f(x: ridl_rt::port::ServeError) {
215///     match x {
216///         ridl_rt::port::ServeError::Contract(_) => {}
217///         ridl_rt::port::ServeError::NotOwner => {}
218///         ridl_rt::port::ServeError::Detached => {}
219///     }
220/// }
221/// ```
222///
223/// ```compile_fail
224/// fn f(x: ridl_rt::port::SettleError) {
225///     match x {
226///         ridl_rt::port::SettleError::UnknownClaim => {}
227///         ridl_rt::port::SettleError::TooLarge { .. } => {}
228///         ridl_rt::port::SettleError::Detached => {}
229///     }
230/// }
231/// ```
232///
233/// ```compile_fail
234/// fn f(x: ridl_rt::error::ClientError) {
235///     match x {
236///         ridl_rt::error::ClientError::Send(_) => {}
237///         ridl_rt::error::ClientError::Call(_) => {}
238///         ridl_rt::error::ClientError::Read(_) => {}
239///     }
240/// }
241/// ```
242///
243/// ```compile_fail
244/// fn f(x: ridl_rt::error::ProviderError) {
245///     match x {
246///         ridl_rt::error::ProviderError::Serve(_) => {}
247///         ridl_rt::error::ProviderError::Claim(_) => {}
248///     }
249/// }
250/// ```
251///
252/// The same ten matches, each with a `_` arm, compile: every path and every
253/// variant name above resolves, so a block above fails only because it names
254/// no `_` arm against a `#[non_exhaustive]` enum.
255///
256/// ```
257/// fn read_error(x: ridl_rt::port::ReadError) {
258///     match x {
259///         ridl_rt::port::ReadError::Short { .. } => {}
260///         ridl_rt::port::ReadError::ShortClaim { .. } => {}
261///         ridl_rt::port::ReadError::TooFewSamples { .. } => {}
262///         ridl_rt::port::ReadError::Contract(_) => {}
263///         ridl_rt::port::ReadError::Detached => {}
264///         _ => {}
265///     }
266/// }
267/// fn write_error(x: ridl_rt::port::WriteError) {
268///     match x {
269///         ridl_rt::port::WriteError::TooLarge { .. } => {}
270///         ridl_rt::port::WriteError::NotOwner => {}
271///         ridl_rt::port::WriteError::Contract(_) => {}
272///         ridl_rt::port::WriteError::Detached => {}
273///         _ => {}
274///     }
275/// }
276/// fn raise_error(x: ridl_rt::port::RaiseError) {
277///     match x {
278///         ridl_rt::port::RaiseError::Busy => {}
279///         ridl_rt::port::RaiseError::TooLarge { .. } => {}
280///         ridl_rt::port::RaiseError::NotOwner => {}
281///         ridl_rt::port::RaiseError::Contract(_) => {}
282///         ridl_rt::port::RaiseError::Detached => {}
283///         _ => {}
284///     }
285/// }
286/// fn send_error(x: ridl_rt::port::SendError) {
287///     match x {
288///         ridl_rt::port::SendError::Busy => {}
289///         ridl_rt::port::SendError::TooLarge { .. } => {}
290///         ridl_rt::port::SendError::Contract(_) => {}
291///         ridl_rt::port::SendError::Detached => {}
292///         _ => {}
293///     }
294/// }
295/// fn subscribe_error(x: ridl_rt::port::SubscribeError) {
296///     match x {
297///         ridl_rt::port::SubscribeError::Contract(_) => {}
298///         ridl_rt::port::SubscribeError::Detached => {}
299///         _ => {}
300///     }
301/// }
302/// fn serve_error(x: ridl_rt::port::ServeError) {
303///     match x {
304///         ridl_rt::port::ServeError::Contract(_) => {}
305///         ridl_rt::port::ServeError::NotOwner => {}
306///         ridl_rt::port::ServeError::Detached => {}
307///         _ => {}
308///     }
309/// }
310/// fn settle_error(x: ridl_rt::port::SettleError) {
311///     match x {
312///         ridl_rt::port::SettleError::UnknownClaim => {}
313///         ridl_rt::port::SettleError::TooLarge { .. } => {}
314///         ridl_rt::port::SettleError::Detached => {}
315///         _ => {}
316///     }
317/// }
318/// fn transport(x: ridl_rt::error::Transport) {
319///     match x {
320///         ridl_rt::error::Transport::Timeout => {}
321///         ridl_rt::error::Transport::Undelivered => {}
322///         ridl_rt::error::Transport::Down => {}
323///         ridl_rt::error::Transport::Corrupt => {}
324///         ridl_rt::error::Transport::Busy => {}
325///         _ => {}
326///     }
327/// }
328/// fn client_error(x: ridl_rt::error::ClientError) {
329///     match x {
330///         ridl_rt::error::ClientError::Send(_) => {}
331///         ridl_rt::error::ClientError::Call(_) => {}
332///         ridl_rt::error::ClientError::Read(_) => {}
333///         _ => {}
334///     }
335/// }
336/// fn provider_error(x: ridl_rt::error::ProviderError) {
337///     match x {
338///         ridl_rt::error::ProviderError::Serve(_) => {}
339///         ridl_rt::error::ProviderError::Claim(_) => {}
340///         _ => {}
341///     }
342/// }
343/// ```
344///
345/// `Contract` and `CallError` stay exhaustive under R-11: a match naming
346/// every variant, with no `_` arm, compiles.
347///
348/// ```
349/// fn contract(x: ridl_rt::error::Contract) {
350///     match x {
351///         ridl_rt::error::Contract::InvalidValue(_) => {}
352///         ridl_rt::error::Contract::PreconditionFailed => {}
353///         ridl_rt::error::Contract::ContractBroken => {}
354///         ridl_rt::error::Contract::UnknownInteraction => {}
355///     }
356/// }
357/// fn call_error(x: ridl_rt::error::CallError) {
358///     match x {
359///         ridl_rt::error::CallError::Contract(_) => {}
360///         ridl_rt::error::CallError::Transport(_) => {}
361///     }
362/// }
363/// ```
364///
365/// `port::Interest` is exhaustive too, because a runtime must handle every key
366/// (ADR-0021 decision 13): a match naming every key, with no `_` arm, compiles.
367///
368/// ```
369/// fn interest(x: ridl_rt::port::Interest) {
370///     match x {
371///         ridl_rt::port::Interest::Outcome(_) => {}
372///         ridl_rt::port::Interest::Slot => {}
373///         ridl_rt::port::Interest::Event(_) => {}
374///         ridl_rt::port::Interest::Claim(_) => {}
375///     }
376/// }
377/// ```
378#[cfg(doctest)]
379mod exhaustiveness {}