Skip to main content

ridl_rt/
port.rs

1//! The ports: the traits a runtime implements and generated code calls.
2//!
3//! Every port method returns without waiting. Waiting — blocking, `async`, or
4//! a loop that drives the runtime — belongs to a face over a port, and this
5//! crate defines no face. A port carries interface numbers, ordinals and
6//! bytes, never a payload type: the generated binding decodes. No port method
7//! takes the current time; a runtime reads its own [`Clock`].
8//!
9//! A port is attached to one catalog ([`Attached`]), and the interface numbers
10//! and ordinals its methods take are scoped by that catalog.
11//!
12//! [`ScannableSignals`] and [`CoherentSignals`] are extensions. They describe
13//! mechanisms some runtimes have, not interaction semantics every runtime must
14//! present, so a runtime may omit them. [`Wakeable`] is an extension too: it
15//! is how a face that waits learns when to read a port again, and a runtime
16//! that serves a generated async client implements it.
17//!
18//! Each trait below names the generated method it backs, so a reader who
19//! arrived from generated code can find the port under it. The crate-level
20//! documentation has the whole table, and
21//! `docs/technotes/ridl-rt-by-example.md` in this repository walks it against
22//! concrete generated code.
23
24use core::task::Waker;
25
26use crate::contract::{CatalogRef, InterfaceNo, Ordinal};
27use crate::error::{CallError, Contract};
28use crate::sample::{Duration, Envelope, Freshness, Provenance, Timestamp};
29use crate::trace::TraceContext;
30
31/// A port attached to one catalog.
32pub trait Attached {
33    /// The catalog this port serves.
34    fn catalog(&self) -> &CatalogRef;
35}
36
37/// The clock envelopes are stamped from. A runtime has one.
38pub trait Clock {
39    /// The current time in the platform time base.
40    fn now(&self) -> Timestamp;
41}
42
43/// Signals, consumer side.
44///
45/// A generated `Client` has one method per signal over this port. It reads
46/// into a stack buffer sized from the payload's
47/// [`MAX_SIZE`](crate::payload::Payload::MAX_SIZE), checks the bytes, and
48/// returns a [`Sample`](crate::sample::Sample). Bytes that fail their check
49/// are not an error there: the sample carries the channel's init value and a
50/// provenance of [`Invalid`](crate::sample::Provenance::Invalid), so the
51/// [`ReadError`] here reports the read itself.
52pub trait SignalReader: Attached {
53    /// Copies the signal's current value into the front of `out` and returns
54    /// its provenance, its freshness and its envelope. The runtime resolves
55    /// both the provenance and the freshness.
56    ///
57    /// A sample whose provenance is `Init` or `Invalid(Declared)` may carry
58    /// zero bytes, because the runtime has no value to copy; `len` is then 0,
59    /// and a generated face returns the channel's init value under that
60    /// provenance (ADR-0021 decision 17).
61    ///
62    /// Returns `ReadError::Short` when `out` is shorter than the value.
63    fn read(
64        &self,
65        iface: InterfaceNo,
66        ord: Ordinal,
67        out: &mut [u8],
68    ) -> Result<RawSample, ReadError>;
69}
70
71/// What [`SignalReader::read`] returns beside the copied bytes.
72#[derive(Clone, Copy, Debug, PartialEq, Eq)]
73pub struct RawSample {
74    /// Where the value comes from.
75    pub provenance: Provenance,
76    /// How old the value is.
77    pub freshness: Freshness,
78    /// The sender's timestamp and sequence number.
79    pub envelope: Envelope,
80    /// The number of bytes copied into `out`.
81    pub len: usize,
82}
83
84/// Signals, provider side.
85///
86/// `set`, `invalidate` and `touch` stage a change. Each fails with
87/// [`WriteError::Contract`] when the ordinal names no member, and with
88/// [`WriteError::NotOwner`] when it names a member this provider does not
89/// own. `commit` publishes every staged change, with one generation
90/// increment and one timestamp per interface, taken from the runtime's
91/// [`Clock`].
92///
93/// A generated `Publisher` has one method per signal over `set`, plus an
94/// `invalidate_<name>` and a `commit`. The staging split is why a provider
95/// that updates several signals of one interface and then commits produces one
96/// coherent publication rather than several.
97pub trait SignalWriter: Attached {
98    /// Stages a new value.
99    fn set(&mut self, iface: InterfaceNo, ord: Ordinal, bytes: &[u8]) -> Result<(), WriteError>;
100    /// Stages the invalid state (ridl §4.5), with
101    /// [`Cause::Declared`](crate::sample::Cause::Declared).
102    fn invalidate(&mut self, iface: InterfaceNo, ord: Ordinal) -> Result<(), WriteError>;
103    /// Stages a re-affirmation of the current value, without a new value.
104    fn touch(&mut self, iface: InterfaceNo, ord: Ordinal) -> Result<(), WriteError>;
105    /// Publishes everything staged. It cannot fail, so it reports nothing —
106    /// including that the runtime behind the port is gone. A caller learns
107    /// that from [`WriteError::Detached`] on a later `set`, `invalidate` or
108    /// `touch`, never from that `commit`. What becomes of the changes staged
109    /// before a commit whose runtime has already detached is not fixed here.
110    fn commit(&mut self);
111}
112
113/// Events, consumer side.
114///
115/// A generated `Client` has a `subscribe_<name>` per event, but a single
116/// `next_event` for the whole interface, because [`next`](EventSource::next)
117/// returns the next occurrence of anything subscribed and the payload type is
118/// not known until its ordinal has been read. The generated method routes on
119/// that ordinal into an enum with one variant per event.
120///
121/// # Trace context delivery
122///
123/// - A runtime that carries the trace context delivers, on every
124///   [`RawOccurrence`] a `raise` produces (one for each subscriber), the
125///   value its sender passed, unchanged.
126/// - A runtime or transport that does not carry the trace context delivers
127///   `None`.
128/// - A sender's `None` is delivered as `None`.
129pub trait EventSource: Attached {
130    /// Starts delivery of the listed events.
131    fn subscribe(&mut self, iface: InterfaceNo, ords: &[Ordinal]) -> Result<(), SubscribeError>;
132    /// Stops delivery of the listed events.
133    fn unsubscribe(&mut self, iface: InterfaceNo, ords: &[Ordinal]);
134    /// Copies the next occurrence into the front of `out`. `Ok(None)` when no
135    /// occurrence is waiting.
136    ///
137    /// Occurrences come in order, and a gap in `seq` is a loss (ridl §3.1). An
138    /// occurrence older than its time to live is discarded here (ridl §5.2).
139    /// `ReadError::Short` does not consume the occurrence: the next call
140    /// returns the same one.
141    fn next(&mut self, out: &mut [u8]) -> Result<Option<RawOccurrence>, ReadError>;
142}
143
144/// What [`EventSource::next`] returns beside the copied bytes.
145#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146pub struct RawOccurrence {
147    /// The interface of the event.
148    pub iface: InterfaceNo,
149    /// The ordinal of the event.
150    pub ord: Ordinal,
151    /// The sender's timestamp and sequence number.
152    pub envelope: Envelope,
153    /// The trace context the sender passed to `raise`, or `None` (see the
154    /// delivery rules on [`EventSource`]).
155    pub trace: Option<TraceContext>,
156    /// The number of bytes copied into `out`.
157    pub len: usize,
158}
159
160/// Events, provider side.
161///
162/// A generated `Publisher` has one method per event over this port. Unlike a
163/// signal, an occurrence is not staged and there is no `commit`: there is no
164/// coherent set to assemble.
165///
166/// # Trace context delivery
167///
168/// - A runtime that carries the trace context delivers, on every
169///   [`RawOccurrence`] a `raise` produces (one for each subscriber), the
170///   value its sender passed, unchanged.
171/// - A runtime or transport that does not carry the trace context delivers
172///   `None`.
173/// - A sender's `None` is delivered as `None`.
174pub trait EventSink: Attached {
175    /// Raises one occurrence.
176    fn raise(
177        &mut self,
178        iface: InterfaceNo,
179        ord: Ordinal,
180        bytes: &[u8],
181        trace: Option<TraceContext>,
182    ) -> Result<(), RaiseError>;
183}
184
185/// Calls, consumer side.
186///
187/// A command and a query are separate methods, because their outcomes differ
188/// (ridl §6, §7).
189///
190/// A generated `Client` has one method per command and query over this port,
191/// each returning a named future rather than an outcome, because nothing
192/// here waits. The method sends when it is called; the future's `poll` reads
193/// the outcome — [`ack`](Caller::ack) for a command, [`reply`](Caller::reply)
194/// for a query — and calls [`forget`](Caller::forget) when it leaves the
195/// waiting phase. A `require` clause is evaluated before sending, so a
196/// failing precondition costs no round trip and is reported as
197/// [`SendError::Contract`].
198///
199/// # Trace context delivery
200///
201/// - A runtime that carries the trace context delivers, on the [`Claim`] a
202///   command or query produces, the value its sender passed, unchanged.
203/// - A runtime or transport that does not carry the trace context delivers
204///   `None`.
205/// - A sender's `None` is delivered as `None`.
206pub trait Caller: Attached {
207    /// Sends a command and returns the correlation of its outcome.
208    fn command(
209        &mut self,
210        iface: InterfaceNo,
211        ord: Ordinal,
212        args: &[u8],
213        trace: Option<TraceContext>,
214    ) -> Result<Correlation, SendError>;
215    /// Sends a query and returns the correlation of its reply.
216    fn query(
217        &mut self,
218        iface: InterfaceNo,
219        ord: Ordinal,
220        args: &[u8],
221        trace: Option<TraceContext>,
222    ) -> Result<Correlation, SendError>;
223    /// A command's delivery acknowledgment (ridl §6.1), once it is known:
224    /// `Ok(())` when accepted, `Err(CallError::Contract(_))` when rejected,
225    /// `Err(CallError::Transport(Transport::Corrupt))` when the provider could
226    /// not read the command's argument bytes,
227    /// `Err(CallError::Transport(Transport::Busy))` when the providing runtime
228    /// refused the command at admission, and
229    /// `Err(CallError::Transport(Transport::Undelivered))` when no
230    /// acknowledgment came within the bound. `None` while unknown, and always
231    /// `None` for a query's correlation.
232    ///
233    /// `None` has two causes this method does not separate: the acknowledgment
234    /// is not known yet, and `c` is a query's correlation, for which `None` is
235    /// the standing answer. A caller that polls `ack` for a query's
236    /// correlation therefore never finishes. The return carries no error, so
237    /// keep the correlations [`command`](Caller::command) returned and ask
238    /// only about those. After [`forget`](Caller::forget) a correlation's
239    /// outcome is no longer retrievable, so do not ask about it.
240    fn ack(&mut self, c: Correlation) -> Option<Result<(), CallError>>;
241    /// A query's reply, once it is known: the reply bytes copied into the front
242    /// of `out` and their length, or the error. `Ok(None)` while unknown.
243    /// `ReadError::Short` does not consume the reply.
244    ///
245    /// The outer [`ReadError`] reports the port call itself: `Short` when
246    /// `out` is too short, and `Detached` when the local runtime is gone.
247    /// `reply` never returns `ReadError::Contract`. The inner [`CallError`] is
248    /// the outcome from the peer or the transport, such as a contract error
249    /// the provider settled, or `Transport::Down` when the connection to the
250    /// peer is lost.
251    fn reply(
252        &mut self,
253        c: Correlation,
254        out: &mut [u8],
255    ) -> Result<Option<Result<usize, CallError>>, ReadError>;
256    /// Releases a correlation whose outcome the caller no longer needs.
257    fn forget(&mut self, c: Correlation);
258}
259
260/// Identifies one sent call to its caller.
261#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
262pub struct Correlation(pub u64);
263
264/// Calls, provider side.
265///
266/// `next_claim` presents each delivered call once. A retransmission of a call
267/// already presented is not presented again and receives the cached
268/// acknowledgment. Two calls are presented again. One is a claim a dropped
269/// handler held and did not settle: the runtime returns it to the waiting
270/// calls, so another handler that serves the member can take it, as many
271/// times as a holder is dropped (ADR-0021 decision 5). The other is a claim
272/// offered through [`ReadError::ShortClaim`], which stays the next call, under
273/// the same id, until it is read with a large enough buffer or settled by
274/// that id (decision 5, amended 2026-09-28). Calls from two callers are never
275/// merged, even when they carry the same `seq`. A call lost in transport is never presented. The
276/// caller of a lost command sees `Transport::Undelivered` from `Caller::ack`;
277/// the caller of a lost query sees `Transport::Timeout` from `Caller::reply`
278/// once the response bound passes.
279///
280/// Every claim is settled. For a command, the generated dispatch settles
281/// `Ok(&[])` after the arguments and `require` pass, before application code
282/// runs. For a query, it settles with the reply bytes or the outcome the
283/// caller sees.
284///
285/// An application implements neither this trait nor a claim loop. It
286/// implements the generated `Provider` trait and polls the future the
287/// generated `serve` returns, which on each poll makes one pass over the
288/// claims already waiting, routes each by ordinal, decodes, evaluates
289/// `require`, calls the provider, evaluates a query's `ensure`, and settles.
290/// That future resolves in two cases only: at once, when this port's `serve`
291/// refused the members, and later, when this port fails.
292///
293/// # Trace context delivery
294///
295/// - A runtime that carries the trace context delivers, on the [`Claim`] a
296///   command or query produces, the value its sender passed, unchanged. The
297///   same holds for the `trace` field of [`ReadError::ShortClaim`], which
298///   reports that claim before its arguments are read.
299/// - A runtime or transport that does not carry the trace context delivers
300///   `None`.
301/// - A sender's `None` is delivered as `None`.
302pub trait Handler: Attached {
303    /// Starts presenting calls to the listed members.
304    fn serve(&mut self, iface: InterfaceNo, ords: &[Ordinal]) -> Result<(), ServeError>;
305    /// Copies the next call's arguments into the front of `out`. `Ok(None)`
306    /// when no call is waiting. When `out` is shorter than the next call's
307    /// arguments, returns [`ReadError::ShortClaim`] with that call's
308    /// `ClaimId` and the bytes it needs, and does not consume the call: a
309    /// later `next_claim` with a buffer of at least `needed` bytes presents
310    /// the same call under the same id. The id is assigned when the call is
311    /// first presented, whether through `ShortClaim` or through `Ok(Some)`,
312    /// and is unique in its channel. `next_claim` never returns
313    /// `ReadError::Short`.
314    fn next_claim(&mut self, out: &mut [u8]) -> Result<Option<Claim>, ReadError>;
315    /// Settles a claim with the reply bytes (empty for a command) or the
316    /// outcome the caller sees. A provider settles
317    /// [`CallError::Contract`] when the arguments break their typl
318    /// constraints, a `require` clause fails, or an `ensure` clause fails,
319    /// and `CallError::Transport(Transport::Corrupt)` when the argument
320    /// bytes fail the structure check.
321    ///
322    /// A claim presented through [`ReadError::ShortClaim`] is settled the
323    /// same way, with any outcome, although its arguments were never read:
324    /// `settle` does not distinguish a read claim from an unread one. The
325    /// settlement takes the call out of the waiting calls, so a later
326    /// `next_claim` does not present it.
327    fn settle(
328        &mut self,
329        claim: ClaimId,
330        outcome: Result<&[u8], CallError>,
331    ) -> Result<(), SettleError>;
332}
333
334/// One call presented to a provider.
335#[derive(Clone, Copy, Debug, PartialEq, Eq)]
336pub struct Claim {
337    /// Unique in its channel.
338    pub id: ClaimId,
339    /// The interface of the call.
340    pub iface: InterfaceNo,
341    /// The ordinal of the call. The descriptor at this ordinal gives the kind.
342    pub ord: Ordinal,
343    /// The caller's timestamp and sequence number. `seq` is unique for each
344    /// caller, not for each channel.
345    pub envelope: Envelope,
346    /// The trace context the sender passed to `command` or `query`, or `None`
347    /// (see the delivery rules on [`Handler`]).
348    pub trace: Option<TraceContext>,
349    /// The time left before the response bound passes. `None` when the call
350    /// has no response bound, as in a catalog built before commands and queries
351    /// took a default one (ridl §9.3).
352    pub remaining: Option<Duration>,
353    /// The number of argument bytes copied into `out`.
354    pub len: usize,
355}
356
357/// Identifies one claim to its provider.
358#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
359pub struct ClaimId(pub u64);
360
361/// `fixed`, consumer side (ridl §8).
362///
363/// The generated face carries no method over this port in this version: a
364/// `fixed` is provisioned rather than interacted with, so the emitter writes
365/// its descriptor and nothing else, and an application that needs a
366/// provisioned value calls [`read_fixed`](FixedReader::read_fixed) itself.
367/// There is no provider side either — a provisioned constant is supplied to
368/// the runtime, not published by application code.
369pub trait FixedReader: Attached {
370    /// Copies the provisioned value into the front of `out` and returns its
371    /// length.
372    fn read_fixed(
373        &self,
374        iface: InterfaceNo,
375        ord: Ordinal,
376        out: &mut [u8],
377    ) -> Result<usize, ReadError>;
378}
379
380/// Extension: signals in a store a consumer can walk. A runtime may omit it.
381pub trait ScannableSignals: SignalReader {
382    /// The interface's generation: a counter that each commit to the interface
383    /// increments.
384    ///
385    /// The return carries no error, so an interface the port's catalog does
386    /// not hold has no reserved answer: it gives a `u64` the caller cannot
387    /// tell from a real generation. Ask only about an interface of
388    /// [`catalog`](Attached::catalog).
389    fn generation(&self, iface: InterfaceNo) -> u64;
390    /// Writes the changes into `out`, interface by interface, in the order of
391    /// `marks`, and updates `marks`. An interface's changes are written all
392    /// together or not at all: when they do not fit in the rest of `out`,
393    /// none of them is written, that interface's mark is not updated, and
394    /// `scan` returns the number of entries written so far.
395    ///
396    /// The count alone does not say whether changes are still waiting, because
397    /// a return of 0 has two causes. To tell them apart, compare each mark
398    /// with its interface's [`generation`](ScannableSignals::generation) after
399    /// the call:
400    ///
401    /// - every mark's `generation` equals `generation(iface)` — nothing had
402    ///   changed, and the scan is complete;
403    /// - some mark's `generation` is behind `generation(iface)` — that
404    ///   interface's changes did not fit in `out`. When `scan` also returned
405    ///   0, `out` is shorter than the changes of the first such interface in
406    ///   the order of `marks`, and no call can make progress until `out` is
407    ///   longer.
408    ///
409    /// A caller that scans in a loop therefore grows `out` when `scan` returns
410    /// 0 and a mark is still behind its interface's generation.
411    fn scan(&self, marks: &mut [Watermark], out: &mut [Changed]) -> usize;
412}
413
414/// How far a consumer has scanned one interface.
415#[derive(Clone, Copy, Debug, PartialEq, Eq)]
416pub struct Watermark {
417    /// The interface.
418    pub iface: InterfaceNo,
419    /// The generation last scanned. Named `generation`, not `gen`, because
420    /// `gen` is a reserved keyword in the 2024 edition.
421    pub generation: u64,
422    /// The sequence number last scanned.
423    pub seq: u64,
424}
425
426/// A signal that changed since a [`Watermark`].
427#[derive(Clone, Copy, Debug, PartialEq, Eq)]
428pub struct Changed {
429    /// The interface of the signal.
430    pub iface: InterfaceNo,
431    /// The ordinal of the signal.
432    pub ord: Ordinal,
433    /// The signal's sequence number.
434    pub seq: u64,
435}
436
437/// Extension: reads of several signals of one interface from one publication.
438/// A runtime whose binding delivers each field separately cannot present this
439/// and omits it.
440pub trait CoherentSignals: SignalReader {
441    /// Answers every ordinal in `ords` from one publication. Copies the values
442    /// into `out` one after another, writes one `RawSample` for each ordinal
443    /// into `samples` in the order of `ords`, and returns the number of bytes
444    /// written to `out`.
445    ///
446    /// Returns `ReadError::Short` with the size the whole set needs when `out`
447    /// is too short, `ReadError::TooFewSamples` with the number of entries
448    /// `samples` needs when `samples` is shorter than `ords`, and
449    /// `ReadError::Contract(Contract::UnknownInteraction)` when an ordinal
450    /// names no member.
451    fn read_coherent(
452        &self,
453        iface: InterfaceNo,
454        ords: &[Ordinal],
455        out: &mut [u8],
456        samples: &mut [RawSample],
457    ) -> Result<usize, ReadError>;
458}
459
460/// Extension: a port that can wake a task. A runtime that serves a generated
461/// async client implements it.
462///
463/// No port method waits, so a task registers its interest here, reads the
464/// port, and returns when the read finds nothing; the runtime wakes the task
465/// when the thing it waits for may have changed, and the task reads the port
466/// again.
467///
468/// The contract:
469///
470/// - **One waker per kind of key per handle.** [`wake_on`](Wakeable::wake_on)
471///   stores a clone of `waker` on the handle it is called on, one for each
472///   kind — `Slot`, `Event`, `Claim` — and an `Outcome` waker with its call.
473///   A change to any key of that kind that the handle observes wakes the
474///   stored waker, so a task that registers `Event(a)` and then `Event(b)` is
475///   woken by an occurrence of either; the task reads the port again and
476///   finds out which.
477/// - **A refresh or a displacement.** A `wake_on` whose waker
478///   [`will_wake`](Waker::will_wake) the stored one is a refresh:
479///   it replaces the stored waker without waking it, because a task
480///   registers on every poll and waking it for its own registration would
481///   schedule the next poll from every poll. A waker of another task
482///   displaces the stored one, and the displaced waker is woken, so no task
483///   waits on a registration that can no longer fire. A second task waiting
484///   for the same events holds a second handle, and each handle's waiter is
485///   woken.
486/// - **Woken at most once.** A stored waker is woken after every change of
487///   its kind becomes visible, and is cleared when woken. A spurious wake is
488///   allowed: a runtime with one unkeyed "something changed" source may wake
489///   every waiter it holds on any change.
490/// - **Register, then read.** The caller registers on every poll, and
491///   registers before it reads the port, so a change between the read and
492///   the return still wakes it.
493///
494/// [`Interest::Event`] and [`Interest::Claim`] are keyed per interface,
495/// because [`EventSource::next`] and [`Handler::next_claim`] drain one queue
496/// whatever the ordinal, and the subscription and the served set already
497/// filter by member.
498pub trait Wakeable {
499    /// Wakes `waker` when the thing `what` names may have changed, under the
500    /// contract above.
501    fn wake_on(&self, what: Interest, waker: &Waker);
502}
503
504/// What a task waits for, as [`Wakeable::wake_on`] takes it.
505///
506/// Exhaustive: a runtime handles every key, because an unknown key has no
507/// safe default. Ignoring it leaves the waiter waiting, and waking it at once
508/// makes a busy loop. A new key is a 0.x minor (ADR-0021 decision 13).
509#[derive(Clone, Copy, Debug, PartialEq, Eq)]
510pub enum Interest {
511    /// The outcome of one call is known.
512    Outcome(Correlation),
513    /// A slot for a new call is free.
514    Slot,
515    /// An occurrence of one of the interface's events is waiting.
516    Event(InterfaceNo),
517    /// A claim on one of the interface's members is waiting.
518    Claim(InterfaceNo),
519}
520
521/// A read that failed.
522///
523/// One enum serves every read on every port, so a variant can be unreachable
524/// for the method that returns it: [`TooFewSamples`](ReadError::TooFewSamples)
525/// belongs to [`CoherentSignals::read_coherent`] alone, and
526/// [`Caller::reply`] never returns [`Contract`](ReadError::Contract). Each
527/// method documents what it can return.
528#[non_exhaustive]
529#[derive(Clone, Copy, Debug, PartialEq, Eq)]
530pub enum ReadError {
531    /// The output buffer is too short. Nothing was consumed.
532    Short {
533        /// The bytes the read needs.
534        needed: usize,
535    },
536    /// The output buffer is too short for the next claim's arguments
537    /// ([`Handler::next_claim`] alone). Nothing was consumed: the claim stays
538    /// the next one, and a later `next_claim` with a buffer of at least
539    /// `needed` bytes presents it under the same `claim`. The id is reported
540    /// so that a provider can settle the claim without reading its
541    /// arguments; the generated `serve` settles it
542    /// `CallError::Transport(Transport::Corrupt)`, because an argument that
543    /// does not fit the serving interface's `MAX_BUFFER_SIZE` — its largest
544    /// argument or reply payload, larger than any valid encoding of its
545    /// members — is not a well-formed encoding of one; a claim naming another
546    /// interface may be validly larger, and is settled the same, because the
547    /// serving step cannot read it (ADR-0021 decision 5, amended
548    /// 2026-09-28).
549    ShortClaim {
550        /// The claim whose arguments did not fit.
551        claim: ClaimId,
552        /// The bytes the read needs.
553        needed: usize,
554        /// The trace context the sender passed to `command` or `query`, or
555        /// `None`, under the delivery rules on [`Handler`] that apply to
556        /// [`Claim::trace`]. A provider that settles the claim without
557        /// reading it has the context here.
558        trace: Option<TraceContext>,
559    },
560    /// `samples` has fewer entries than `ords`. Nothing was consumed.
561    TooFewSamples {
562        /// The entries `samples` needs.
563        needed: usize,
564    },
565    /// A contract error, such as an unknown interaction.
566    Contract(Contract),
567    /// The runtime behind the port is gone.
568    Detached,
569}
570
571/// A [`SignalWriter::set`], [`SignalWriter::invalidate`] or
572/// [`SignalWriter::touch`] that failed.
573#[non_exhaustive]
574#[derive(Clone, Copy, Debug, PartialEq, Eq)]
575pub enum WriteError {
576    /// The value is larger than the signal's capacity.
577    TooLarge {
578        /// The capacity in bytes.
579        cap: usize,
580    },
581    /// This provider does not own the signal.
582    NotOwner,
583    /// A contract error, such as an unknown interaction.
584    Contract(Contract),
585    /// The runtime behind the port is gone.
586    Detached,
587}
588
589/// An [`EventSink::raise`] that failed.
590#[non_exhaustive]
591#[derive(Clone, Copy, Debug, PartialEq, Eq)]
592pub enum RaiseError {
593    /// The runtime cannot accept an occurrence now. Retryable.
594    Busy,
595    /// The occurrence is larger than the event's capacity.
596    TooLarge {
597        /// The capacity in bytes.
598        cap: usize,
599    },
600    /// This provider does not own the event.
601    NotOwner,
602    /// A contract error, such as an unknown interaction.
603    Contract(Contract),
604    /// The runtime behind the port is gone.
605    Detached,
606}
607
608/// A [`Caller::command`] or [`Caller::query`] that failed before sending.
609#[non_exhaustive]
610#[derive(Clone, Copy, Debug, PartialEq, Eq)]
611pub enum SendError {
612    /// The runtime cannot accept a call now. Retryable.
613    Busy,
614    /// The arguments are larger than the call's capacity.
615    TooLarge {
616        /// The capacity in bytes.
617        cap: usize,
618    },
619    /// A contract error, such as an unknown interaction.
620    Contract(Contract),
621    /// The runtime behind the port is gone.
622    Detached,
623}
624
625/// An [`EventSource::subscribe`] that failed.
626#[non_exhaustive]
627#[derive(Clone, Copy, Debug, PartialEq, Eq)]
628pub enum SubscribeError {
629    /// A contract error: an unknown interaction fails when subscribing (ridl
630    /// §10.2).
631    Contract(Contract),
632    /// The runtime behind the port is gone.
633    Detached,
634}
635
636/// A [`Handler::serve`] that failed.
637#[non_exhaustive]
638#[derive(Clone, Copy, Debug, PartialEq, Eq)]
639pub enum ServeError {
640    /// A contract error, such as an unknown interaction.
641    Contract(Contract),
642    /// This provider does not own the call.
643    NotOwner,
644    /// The runtime behind the port is gone.
645    Detached,
646}
647
648/// A [`Handler::settle`] that failed.
649#[non_exhaustive]
650#[derive(Clone, Copy, Debug, PartialEq, Eq)]
651pub enum SettleError {
652    /// The claim was already settled, or was never issued.
653    UnknownClaim,
654    /// The reply is larger than the call's capacity.
655    TooLarge {
656        /// The capacity in bytes.
657        cap: usize,
658    },
659    /// The runtime behind the port is gone.
660    Detached,
661}
662
663// Forwarding impls (ADR-0021 decision 11).
664//
665// Every port trait above is implemented for `&mut P`, and the seven whose
666// methods all take `&self` — `Attached`, `Clock`, `SignalReader`,
667// `FixedReader`, `ScannableSignals`, `CoherentSignals` and `Wakeable` — also
668// for `&P`.
669// What they buy is one thing: a value generic over a port trait, such as a
670// generated face, can be built over a reference to a port rather than over the
671// port itself. A wrapper that adds tracing and a test double are accepted by
672// such a bound with or without them, because each implements the port traits
673// itself.
674//
675// `impl<P: T + ?Sized> T for Box<P>` is deferred: it needs `alloc`, which only
676// the `std` feature brings in, and nothing needs a boxed port (ADR-0021 open
677// question 5).
678
679impl<P: Attached + ?Sized> Attached for &P {
680    fn catalog(&self) -> &CatalogRef {
681        (**self).catalog()
682    }
683}
684
685impl<P: Attached + ?Sized> Attached for &mut P {
686    fn catalog(&self) -> &CatalogRef {
687        (**self).catalog()
688    }
689}
690
691impl<P: Clock + ?Sized> Clock for &P {
692    fn now(&self) -> Timestamp {
693        (**self).now()
694    }
695}
696
697impl<P: Clock + ?Sized> Clock for &mut P {
698    fn now(&self) -> Timestamp {
699        (**self).now()
700    }
701}
702
703impl<P: SignalReader + ?Sized> SignalReader for &P {
704    fn read(
705        &self,
706        iface: InterfaceNo,
707        ord: Ordinal,
708        out: &mut [u8],
709    ) -> Result<RawSample, ReadError> {
710        (**self).read(iface, ord, out)
711    }
712}
713
714impl<P: SignalReader + ?Sized> SignalReader for &mut P {
715    fn read(
716        &self,
717        iface: InterfaceNo,
718        ord: Ordinal,
719        out: &mut [u8],
720    ) -> Result<RawSample, ReadError> {
721        (**self).read(iface, ord, out)
722    }
723}
724
725impl<P: SignalWriter + ?Sized> SignalWriter for &mut P {
726    fn set(&mut self, iface: InterfaceNo, ord: Ordinal, bytes: &[u8]) -> Result<(), WriteError> {
727        (**self).set(iface, ord, bytes)
728    }
729    fn invalidate(&mut self, iface: InterfaceNo, ord: Ordinal) -> Result<(), WriteError> {
730        (**self).invalidate(iface, ord)
731    }
732    fn touch(&mut self, iface: InterfaceNo, ord: Ordinal) -> Result<(), WriteError> {
733        (**self).touch(iface, ord)
734    }
735    fn commit(&mut self) {
736        (**self).commit();
737    }
738}
739
740impl<P: EventSource + ?Sized> EventSource for &mut P {
741    fn subscribe(&mut self, iface: InterfaceNo, ords: &[Ordinal]) -> Result<(), SubscribeError> {
742        (**self).subscribe(iface, ords)
743    }
744    fn unsubscribe(&mut self, iface: InterfaceNo, ords: &[Ordinal]) {
745        (**self).unsubscribe(iface, ords);
746    }
747    fn next(&mut self, out: &mut [u8]) -> Result<Option<RawOccurrence>, ReadError> {
748        (**self).next(out)
749    }
750}
751
752impl<P: EventSink + ?Sized> EventSink for &mut P {
753    fn raise(
754        &mut self,
755        iface: InterfaceNo,
756        ord: Ordinal,
757        bytes: &[u8],
758        trace: Option<TraceContext>,
759    ) -> Result<(), RaiseError> {
760        (**self).raise(iface, ord, bytes, trace)
761    }
762}
763
764impl<P: Caller + ?Sized> Caller for &mut P {
765    fn command(
766        &mut self,
767        iface: InterfaceNo,
768        ord: Ordinal,
769        args: &[u8],
770        trace: Option<TraceContext>,
771    ) -> Result<Correlation, SendError> {
772        (**self).command(iface, ord, args, trace)
773    }
774    fn query(
775        &mut self,
776        iface: InterfaceNo,
777        ord: Ordinal,
778        args: &[u8],
779        trace: Option<TraceContext>,
780    ) -> Result<Correlation, SendError> {
781        (**self).query(iface, ord, args, trace)
782    }
783    fn ack(&mut self, c: Correlation) -> Option<Result<(), CallError>> {
784        (**self).ack(c)
785    }
786    fn reply(
787        &mut self,
788        c: Correlation,
789        out: &mut [u8],
790    ) -> Result<Option<Result<usize, CallError>>, ReadError> {
791        (**self).reply(c, out)
792    }
793    fn forget(&mut self, c: Correlation) {
794        (**self).forget(c);
795    }
796}
797
798impl<P: Handler + ?Sized> Handler for &mut P {
799    fn serve(&mut self, iface: InterfaceNo, ords: &[Ordinal]) -> Result<(), ServeError> {
800        (**self).serve(iface, ords)
801    }
802    fn next_claim(&mut self, out: &mut [u8]) -> Result<Option<Claim>, ReadError> {
803        (**self).next_claim(out)
804    }
805    fn settle(
806        &mut self,
807        claim: ClaimId,
808        outcome: Result<&[u8], CallError>,
809    ) -> Result<(), SettleError> {
810        (**self).settle(claim, outcome)
811    }
812}
813
814impl<P: FixedReader + ?Sized> FixedReader for &P {
815    fn read_fixed(
816        &self,
817        iface: InterfaceNo,
818        ord: Ordinal,
819        out: &mut [u8],
820    ) -> Result<usize, ReadError> {
821        (**self).read_fixed(iface, ord, out)
822    }
823}
824
825impl<P: FixedReader + ?Sized> FixedReader for &mut P {
826    fn read_fixed(
827        &self,
828        iface: InterfaceNo,
829        ord: Ordinal,
830        out: &mut [u8],
831    ) -> Result<usize, ReadError> {
832        (**self).read_fixed(iface, ord, out)
833    }
834}
835
836impl<P: ScannableSignals + ?Sized> ScannableSignals for &P {
837    fn generation(&self, iface: InterfaceNo) -> u64 {
838        (**self).generation(iface)
839    }
840    fn scan(&self, marks: &mut [Watermark], out: &mut [Changed]) -> usize {
841        (**self).scan(marks, out)
842    }
843}
844
845impl<P: ScannableSignals + ?Sized> ScannableSignals for &mut P {
846    fn generation(&self, iface: InterfaceNo) -> u64 {
847        (**self).generation(iface)
848    }
849    fn scan(&self, marks: &mut [Watermark], out: &mut [Changed]) -> usize {
850        (**self).scan(marks, out)
851    }
852}
853
854impl<P: CoherentSignals + ?Sized> CoherentSignals for &P {
855    fn read_coherent(
856        &self,
857        iface: InterfaceNo,
858        ords: &[Ordinal],
859        out: &mut [u8],
860        samples: &mut [RawSample],
861    ) -> Result<usize, ReadError> {
862        (**self).read_coherent(iface, ords, out, samples)
863    }
864}
865
866impl<P: CoherentSignals + ?Sized> CoherentSignals for &mut P {
867    fn read_coherent(
868        &self,
869        iface: InterfaceNo,
870        ords: &[Ordinal],
871        out: &mut [u8],
872        samples: &mut [RawSample],
873    ) -> Result<usize, ReadError> {
874        (**self).read_coherent(iface, ords, out, samples)
875    }
876}
877
878impl<P: Wakeable + ?Sized> Wakeable for &P {
879    fn wake_on(&self, what: Interest, waker: &Waker) {
880        (**self).wake_on(what, waker);
881    }
882}
883
884impl<P: Wakeable + ?Sized> Wakeable for &mut P {
885    fn wake_on(&self, what: Interest, waker: &Waker) {
886        (**self).wake_on(what, waker);
887    }
888}