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 {}