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. A runtime is a separate crate that
7//! implements the traits of the `port` module (ADR-0020 decision 6), and
8//! generated code calls those traits without naming the runtime.
9//!
10//! The crate is `no_std`, allocates nothing, contains no `unsafe` code, and has
11//! no dependency in any feature combination. The cargo features `flatbuffers`,
12//! `proto3` and `repr-c` name the payload encodings. `flatbuffers` enables the
13//! [`flatbuffers`] module, the reading and writing a generated
14//! `Payload<FlatBuffers>` implementation shares; `proto3` and `repr-c` enable
15//! nothing in this version.
16//!
17//! Every public item lives in one of six modules, or in the seventh that the
18//! `flatbuffers` feature adds. Generated code names each
19//! item by its full path, for example `ridl_rt::sample::Sample`, and imports
20//! none, because several names here — `Duration`, `Handler`, `Kind` — are also
21//! names in `core` or in application code.
22//!
23//! # Where to start
24//!
25//! Most of this crate is called by generated code, not by an application. An
26//! application implements one generated `Provider` trait and calls generated
27//! `Client` and `Publisher` methods; those methods call the ports here. So the
28//! shortest path in is to read one interaction kind at a time, from the
29//! generated side:
30//!
31//! | To do this                       | The generated face gives you | Over this port                                |
32//! | -------------------------------- | ---------------------------- | --------------------------------------------- |
33//! | Read a signal                    | `Client::<name>`             | [`port::SignalReader`], returning [`sample::Sample`] |
34//! | Publish a signal                 | `Publisher::<name>`, `commit` | [`port::SignalWriter`]                        |
35//! | Receive an event                 | `Client::subscribe_<name>`, `Client::next_event` | [`port::EventSource`]     |
36//! | Raise an event                   | `Publisher::<name>`          | [`port::EventSink`]                           |
37//! | Call a command or a query        | `Client::<name>`, returning a [`port::Correlation`] | [`port::Caller`]       |
38//! | Serve a command or a query       | `Provider`, driven by the generated `dispatch` | [`port::Handler`]           |
39//! | Read a provisioned constant      | nothing yet — call the port  | [`port::FixedReader`]                         |
40//!
41//! `docs/technotes/ridl-rt-by-example.md` in this repository walks that table
42//! from top to bottom against concrete generated code, introducing each type
43//! at the point where the generated code first needs it. The library's own
44//! as-built description is `docs/design/ridl-rt.md`.
45//!
46//! Two properties hold everywhere and are worth knowing before reading any
47//! individual item. **No port method waits** — every one returns immediately,
48//! and a call's outcome is retrieved separately through a
49//! [`port::Correlation`]. And **no port names a payload type** — ports carry
50//! interface numbers, ordinals and bytes, and the generated binding is what
51//! encodes and decodes, through [`payload::Ref`].
52//!
53//! # How a runtime presents its ports
54//!
55//! A runtime crate exposes one handle type per port role it implements — a
56//! port role is one port trait — rather than one type implementing them all,
57//! and it may also offer an aggregate handle covering the port set one
58//! interface's face needs. A generated face is built over one value
59//! implementing at least the port traits its interface needs: the role handle
60//! itself when the face needs exactly one, and an aggregate — the runtime's,
61//! or one the application writes over role handles — when it needs more,
62//! because a generated `Client` may be bound over `SignalReader`,
63//! `EventSource` and `Caller` at once. Either value reaches the face by value
64//! or as a `&mut` borrow of itself, because every port trait is implemented
65//! for `&mut P` and the `&self`-only traits also for `&P`. In a runtime whose
66//! handles are used from more than one thread, a handle whose port traits all
67//! take `&self` is `Send + Sync`, because several threads may read one store
68//! at once, and a handle carrying a trait with a `&mut self` method is `Send`
69//! and need not be `Sync`, because one thread drives each. No port trait here
70//! carries `Send` or `Sync` as a supertrait, so a single-threaded `no_std`
71//! runtime whose handles use `Cell` or `RefCell` internally is held to
72//! neither; a runtime checks its own handles itself, with a compile-time
73//! assertion. ADR-0021 decision 12 records this.
74
75#![no_std]
76#![forbid(unsafe_code)]
77pub mod contract;
78pub mod encoding;
79pub mod error;
80#[cfg(feature = "flatbuffers")]
81pub mod flatbuffers;
82pub mod payload;
83pub mod port;
84pub mod sample;
85
86/// Pins which enums stay `#[non_exhaustive]` under R-11: `Transport`,
87/// `ReadError`, `WriteError`, `RaiseError`, `SendError`, `SubscribeError`,
88/// `ServeError` and `SettleError`. Each `compile_fail` block below matches
89/// every variant of one such enum, with no `_` arm. Matching a
90/// `#[non_exhaustive]` enum from outside its crate with no `_` arm does not
91/// compile, so a block fails until `#[non_exhaustive]` is removed from the
92/// enum it names.
93///
94/// ```compile_fail
95/// fn f(x: ridl_rt::error::Transport) {
96///     match x {
97///         ridl_rt::error::Transport::Timeout => {}
98///         ridl_rt::error::Transport::Undelivered => {}
99///         ridl_rt::error::Transport::Down => {}
100///         ridl_rt::error::Transport::Corrupt => {}
101///     }
102/// }
103/// ```
104///
105/// ```compile_fail
106/// fn f(x: ridl_rt::port::ReadError) {
107///     match x {
108///         ridl_rt::port::ReadError::Short { .. } => {}
109///         ridl_rt::port::ReadError::TooFewSamples { .. } => {}
110///         ridl_rt::port::ReadError::Contract(_) => {}
111///         ridl_rt::port::ReadError::Detached => {}
112///     }
113/// }
114/// ```
115///
116/// ```compile_fail
117/// fn f(x: ridl_rt::port::WriteError) {
118///     match x {
119///         ridl_rt::port::WriteError::TooLarge { .. } => {}
120///         ridl_rt::port::WriteError::NotOwner => {}
121///         ridl_rt::port::WriteError::Contract(_) => {}
122///         ridl_rt::port::WriteError::Detached => {}
123///     }
124/// }
125/// ```
126///
127/// ```compile_fail
128/// fn f(x: ridl_rt::port::RaiseError) {
129///     match x {
130///         ridl_rt::port::RaiseError::Busy => {}
131///         ridl_rt::port::RaiseError::TooLarge { .. } => {}
132///         ridl_rt::port::RaiseError::NotOwner => {}
133///         ridl_rt::port::RaiseError::Contract(_) => {}
134///         ridl_rt::port::RaiseError::Detached => {}
135///     }
136/// }
137/// ```
138///
139/// ```compile_fail
140/// fn f(x: ridl_rt::port::SendError) {
141///     match x {
142///         ridl_rt::port::SendError::Busy => {}
143///         ridl_rt::port::SendError::TooLarge { .. } => {}
144///         ridl_rt::port::SendError::Contract(_) => {}
145///         ridl_rt::port::SendError::Detached => {}
146///     }
147/// }
148/// ```
149///
150/// ```compile_fail
151/// fn f(x: ridl_rt::port::SubscribeError) {
152///     match x {
153///         ridl_rt::port::SubscribeError::Contract(_) => {}
154///         ridl_rt::port::SubscribeError::Detached => {}
155///     }
156/// }
157/// ```
158///
159/// ```compile_fail
160/// fn f(x: ridl_rt::port::ServeError) {
161///     match x {
162///         ridl_rt::port::ServeError::Contract(_) => {}
163///         ridl_rt::port::ServeError::NotOwner => {}
164///         ridl_rt::port::ServeError::Detached => {}
165///     }
166/// }
167/// ```
168///
169/// ```compile_fail
170/// fn f(x: ridl_rt::port::SettleError) {
171///     match x {
172///         ridl_rt::port::SettleError::UnknownClaim => {}
173///         ridl_rt::port::SettleError::TooLarge { .. } => {}
174///         ridl_rt::port::SettleError::Detached => {}
175///     }
176/// }
177/// ```
178///
179/// The same eight matches, each with a `_` arm, compile: every path and every
180/// variant name above resolves, so a block above fails only because it names
181/// no `_` arm against a `#[non_exhaustive]` enum.
182///
183/// ```
184/// fn read_error(x: ridl_rt::port::ReadError) {
185///     match x {
186///         ridl_rt::port::ReadError::Short { .. } => {}
187///         ridl_rt::port::ReadError::TooFewSamples { .. } => {}
188///         ridl_rt::port::ReadError::Contract(_) => {}
189///         ridl_rt::port::ReadError::Detached => {}
190///         _ => {}
191///     }
192/// }
193/// fn write_error(x: ridl_rt::port::WriteError) {
194///     match x {
195///         ridl_rt::port::WriteError::TooLarge { .. } => {}
196///         ridl_rt::port::WriteError::NotOwner => {}
197///         ridl_rt::port::WriteError::Contract(_) => {}
198///         ridl_rt::port::WriteError::Detached => {}
199///         _ => {}
200///     }
201/// }
202/// fn raise_error(x: ridl_rt::port::RaiseError) {
203///     match x {
204///         ridl_rt::port::RaiseError::Busy => {}
205///         ridl_rt::port::RaiseError::TooLarge { .. } => {}
206///         ridl_rt::port::RaiseError::NotOwner => {}
207///         ridl_rt::port::RaiseError::Contract(_) => {}
208///         ridl_rt::port::RaiseError::Detached => {}
209///         _ => {}
210///     }
211/// }
212/// fn send_error(x: ridl_rt::port::SendError) {
213///     match x {
214///         ridl_rt::port::SendError::Busy => {}
215///         ridl_rt::port::SendError::TooLarge { .. } => {}
216///         ridl_rt::port::SendError::Contract(_) => {}
217///         ridl_rt::port::SendError::Detached => {}
218///         _ => {}
219///     }
220/// }
221/// fn subscribe_error(x: ridl_rt::port::SubscribeError) {
222///     match x {
223///         ridl_rt::port::SubscribeError::Contract(_) => {}
224///         ridl_rt::port::SubscribeError::Detached => {}
225///         _ => {}
226///     }
227/// }
228/// fn serve_error(x: ridl_rt::port::ServeError) {
229///     match x {
230///         ridl_rt::port::ServeError::Contract(_) => {}
231///         ridl_rt::port::ServeError::NotOwner => {}
232///         ridl_rt::port::ServeError::Detached => {}
233///         _ => {}
234///     }
235/// }
236/// fn settle_error(x: ridl_rt::port::SettleError) {
237///     match x {
238///         ridl_rt::port::SettleError::UnknownClaim => {}
239///         ridl_rt::port::SettleError::TooLarge { .. } => {}
240///         ridl_rt::port::SettleError::Detached => {}
241///         _ => {}
242///     }
243/// }
244/// fn transport(x: ridl_rt::error::Transport) {
245///     match x {
246///         ridl_rt::error::Transport::Timeout => {}
247///         ridl_rt::error::Transport::Undelivered => {}
248///         ridl_rt::error::Transport::Down => {}
249///         ridl_rt::error::Transport::Corrupt => {}
250///         _ => {}
251///     }
252/// }
253/// ```
254///
255/// `Contract` and `CallError` stay exhaustive under R-11: a match naming
256/// every variant, with no `_` arm, compiles.
257///
258/// ```
259/// fn contract(x: ridl_rt::error::Contract) {
260///     match x {
261///         ridl_rt::error::Contract::InvalidValue(_) => {}
262///         ridl_rt::error::Contract::PreconditionFailed => {}
263///         ridl_rt::error::Contract::ContractBroken => {}
264///         ridl_rt::error::Contract::UnknownInteraction => {}
265///     }
266/// }
267/// fn call_error(x: ridl_rt::error::CallError) {
268///     match x {
269///         ridl_rt::error::CallError::Contract(_) => {}
270///         ridl_rt::error::CallError::Transport(_) => {}
271///     }
272/// }
273/// ```
274#[cfg(doctest)]
275mod exhaustiveness {}