Skip to main content

ridl_rt/
face.rs

1//! The traits a generated face implements: the fixed methods of the
2//! consumer and provider faces the Rust backend emits (ADR-0023 decision 7,
3//! ADR-0021 decision 19).
4//!
5//! A generated `Client` or `Publisher` has two kinds of method. A member
6//! method is derived from the interface — one per signal, event, command and
7//! query, named after the member — and is an inherent method of the type. A
8//! fixed method is the emitter's own — `new`, `next_event`, `with_timeout`,
9//! `set_timeout`, `commit` — and is a method of one of the four traits here.
10//! Rust gives one type one inherent namespace, so a member whose snake case
11//! is `new` or `commit` could not sit beside a fixed inherent method of that
12//! name (rustc E0592); a trait method lives in the trait's namespace, so the
13//! member and the fixed method coexist, and the fixed method stays reachable
14//! through the trait's path. The two derived methods a face adds beside a
15//! member — `subscribe_<event>` and `invalidate_<signal>` — are methods of
16//! two traits the emitter generates inside each interface module,
17//! `Subscribe` and `Invalidate`, for the same reason.
18//!
19//! Which of the two a dot call reaches follows Rust's method probe: it tries
20//! the receiver by value, then by `&`, then by `&mut`, and at each step an
21//! inherent method before a trait method. A member takes `&self` (a signal
22//! read) or `&mut self` (every other member); [`Bind::new`] takes no receiver
23//! and is reached by a path call, and every other trait method here takes
24//! `&mut self` except [`Timeout::with_timeout`], which takes `self` by value.
25//! So the member keeps the dot call for every fixed and derived method but
26//! `with_timeout`: beside a member named `withTimeout`,
27//! `client.with_timeout(x)` on a blocking client held by value reaches the
28//! trait method, because the by-value step comes first, and the consumer
29//! reaches the member through the inherent path
30//! `blocking::Client::with_timeout(&mut client, x)` (`&client` for a signal
31//! read). `with_timeout` takes `self` so that
32//! `Client::new(port).with_timeout(t)` stays one expression.
33//!
34//! A consumer of a generated face writes `use <crate>::<iface>::prelude::*;`
35//! for each interface whose face it uses: the generated `prelude` re-exports
36//! the traits here that the interface's types implement, and the interface's
37//! two generated traits as `_`. rustc reports a prelude as an unused import
38//! when the other imported preludes already bring every item it would add,
39//! and that import can then be dropped. With the
40//! prelude in scope every call site is the one an inherent method had —
41//! `Client::new(port)`, `client.next_event()`, `publisher.commit()`. Only
42//! when a member of the interface is itself named `new` does
43//! `Client::new(port)` resolve to the member, because a path call finds an
44//! inherent item first; the consumer then writes
45//! `<Client<_> as Bind>::new(port)` or `let c: Client<_> = Bind::new(port)`.
46//!
47//! [`Timeout`] is under the `std` feature, because its only implementor is
48//! the generated `blocking` module, which the emitted crate's `std` feature
49//! enables together with this crate's. The other three traits are
50//! unconditional and `no_std`, like the rest of the crate. Nothing here is
51//! implemented by a runtime: a runtime implements the [`port`](crate::port)
52//! traits, and generated code implements these.
53
54/// Binds a face to the port it holds: the `new` of a generated `Client`,
55/// `blocking::Client` and `Publisher`.
56///
57/// `Port` is the type the face is generic over — a runtime's handle, or a
58/// `&mut` borrow of one — and `new` holds it by value.
59///
60/// ```
61/// use ridl_rt::face::Bind;
62///
63/// struct Client<P> { port: P }
64/// impl<P> Bind for Client<P> {
65///     type Port = P;
66///     fn new(port: P) -> Self { Client { port } }
67/// }
68///
69/// let client = Client::new(7u32);
70/// assert_eq!(client.port, 7);
71/// ```
72pub trait Bind {
73    /// The port the face is built over.
74    type Port;
75
76    /// Binds the face to `port`. The port is held by value: pass a handle, or
77    /// a `&mut` borrow of one.
78    fn new(port: Self::Port) -> Self;
79}
80
81/// Takes the next occurrence of a subscribed event: the `next_event` of a
82/// generated `Client` and `blocking::Client`.
83///
84/// `Next` is generic over the borrow of `self`, so one trait serves both
85/// clients: the async client's `Next<'a>` is its event future, which borrows
86/// the client's port, and the blocking client's is the owned
87/// `Result<Option<Event>, ReadError>`. The trait gives the method a namespace
88/// of its own and nothing more: `Next` carries no bound, so code generic over
89/// `Events` cannot use what `next_event` returns.
90///
91/// ```
92/// use ridl_rt::face::Events;
93///
94/// struct Client { queue: Vec<u8> }
95/// impl Events for Client {
96///     type Next<'a> = Option<u8> where Self: 'a;
97///     fn next_event(&mut self) -> Option<u8> { self.queue.pop() }
98/// }
99///
100/// let mut client = Client { queue: vec![1] };
101/// assert_eq!(client.next_event(), Some(1));
102/// assert_eq!(client.next_event(), None);
103/// ```
104pub trait Events {
105    /// What one call of `next_event` returns: a future for the async client,
106    /// an owned result for the blocking one.
107    type Next<'a>
108    where
109        Self: 'a;
110
111    /// Takes the next occurrence of any subscribed event of the interface.
112    fn next_event(&mut self) -> Self::Next<'_>;
113}
114
115/// Bounds every waiting method of a blocking face by a wall-clock timeout:
116/// the `with_timeout` and `set_timeout` of a generated `blocking::Client`.
117///
118/// The timeout is `None` until one of the two sets it; with none, a waiting
119/// method returns only with its outcome. `core::time::Duration` is the type
120/// `std::time::Duration` re-exports, so a consumer passes either.
121///
122/// ```
123/// use core::time::Duration;
124/// use ridl_rt::face::Timeout;
125///
126/// struct Client { timeout: Option<Duration> }
127/// impl Timeout for Client {
128///     fn with_timeout(mut self, timeout: Duration) -> Self {
129///         self.timeout = Some(timeout);
130///         self
131///     }
132///     fn set_timeout(&mut self, timeout: Option<Duration>) { self.timeout = timeout; }
133/// }
134///
135/// let mut client = Client { timeout: None }.with_timeout(Duration::from_millis(5));
136/// assert_eq!(client.timeout, Some(Duration::from_millis(5)));
137/// client.set_timeout(None);
138/// assert_eq!(client.timeout, None);
139/// ```
140#[cfg(feature = "std")]
141pub trait Timeout: Sized {
142    /// Sets the timeout every waiting method of the face is bounded by, and
143    /// returns the face.
144    fn with_timeout(self, timeout: core::time::Duration) -> Self;
145
146    /// Sets or clears the timeout every waiting method of the face is bounded
147    /// by.
148    fn set_timeout(&mut self, timeout: Option<core::time::Duration>);
149}
150
151/// Publishes every staged signal change: the `commit` of a generated
152/// `Publisher`.
153///
154/// ```
155/// use ridl_rt::face::Publish;
156///
157/// struct Publisher { staged: u8, published: u8 }
158/// impl Publish for Publisher {
159///     fn commit(&mut self) { self.published = self.staged; }
160/// }
161///
162/// let mut publisher = Publisher { staged: 3, published: 0 };
163/// publisher.commit();
164/// assert_eq!(publisher.published, 3);
165/// ```
166pub trait Publish {
167    /// Publishes every staged signal change.
168    fn commit(&mut self);
169}