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