ridl_rt/contract.rs
1//! Identity, and the interaction descriptors generated code writes.
2//!
3//! A descriptor is a zero-sized marker type with constants. The descriptors
4//! are the per-member form of the ordinal table of ADR-0013 decision 3,
5//! extended with the interface number and the catalog hash.
6
7use crate::sample::Duration;
8
9/// A member's ordinal: its position in the interface body, counted from 1
10/// (ridl §11). An ordinal is never 0.
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub struct Ordinal(pub u32);
13
14/// An interface's number in its catalog: the number the lock file froze, or a
15/// provisional number. An interface number is never 0.
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
17pub struct InterfaceNo(pub u32);
18
19/// The catalog hash: SHA-256 over the catalog's interfaces, their numbers, and
20/// the types they reach.
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
22pub struct CatalogHash(pub [u8; 32]);
23
24/// A catalog: one package's interfaces.
25///
26/// Two `CatalogRef`s are equal only when both the names and the hashes are
27/// equal, because two packages with the same contents can have the same hash.
28#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
29pub struct CatalogRef {
30 /// The package name.
31 pub name: &'static str,
32 /// The catalog hash.
33 pub hash: CatalogHash,
34}
35
36/// The five interaction kinds of the language.
37#[repr(u8)]
38#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
39pub enum Kind {
40 /// A `signal`.
41 Signal = 1,
42 /// An `event`.
43 Event = 2,
44 /// A `command`.
45 Command = 3,
46 /// A `query`.
47 Query = 4,
48 /// A `fixed`.
49 Fixed = 5,
50}
51
52/// An interface's descriptor.
53pub trait Interface {
54 /// The catalog the interface belongs to.
55 const CATALOG: &'static CatalogRef;
56 /// The interface number in that catalog.
57 const NUMBER: InterfaceNo;
58 /// `true` when the number is provisional, not yet frozen in the lock file.
59 const PROVISIONAL: bool;
60 /// The interface name.
61 const NAME: &'static str;
62 /// The members, in ordinal order. A reserved ordinal has no row, so the
63 /// index of a row is not its ordinal minus one.
64 const MEMBERS: &'static [Member];
65}
66
67/// An interaction's descriptor: the part every kind has.
68pub trait Interaction {
69 /// The interface that declares the interaction.
70 type Iface: Interface;
71 /// The interaction's row in `Iface::MEMBERS`.
72 const MEMBER: &'static Member;
73}
74
75/// A `signal` (ridl §4).
76pub trait Signal: Interaction {
77 /// The value type.
78 type Payload;
79 /// The channel's init value (ridl §4.4).
80 fn init() -> Self::Payload;
81}
82
83/// An `event` (ridl §5).
84pub trait Event: Interaction {
85 /// The occurrence type.
86 type Payload;
87}
88
89/// A `fixed` (ridl §8).
90pub trait Fixed: Interaction {
91 /// The provisioned value type.
92 type Payload;
93}
94
95/// A `command` (ridl §6).
96pub trait Command: Interaction {
97 /// The argument type.
98 type Args;
99 /// Evaluates the command's `require` clauses. `Ok` when every clause is
100 /// true or the command declares none. `Err` when a clause is false, which
101 /// the provider reports as
102 /// [`Contract::PreconditionFailed`](crate::error::Contract::PreconditionFailed).
103 #[allow(
104 clippy::result_unit_err,
105 reason = "the failing method decides the contract error, so the error carries no value"
106 )]
107 fn require(args: &Self::Args) -> Result<(), ()>;
108}
109
110/// A `query` (ridl §7).
111pub trait Query: Interaction {
112 /// The argument type.
113 type Args;
114 /// The reply type.
115 type Reply;
116 /// Evaluates the query's `require` clauses. `Ok` when every clause is
117 /// true or the query declares none. `Err` when a clause is false, which
118 /// the provider reports as
119 /// [`Contract::PreconditionFailed`](crate::error::Contract::PreconditionFailed).
120 #[allow(
121 clippy::result_unit_err,
122 reason = "the failing method decides the contract error, so the error carries no value"
123 )]
124 fn require(args: &Self::Args) -> Result<(), ()>;
125 /// Evaluates the query's `ensure` clauses. `Ok` when every clause is true
126 /// or the query declares none. `Err` when a clause is false, which the
127 /// provider reports as
128 /// [`Contract::ContractBroken`](crate::error::Contract::ContractBroken).
129 #[allow(
130 clippy::result_unit_err,
131 reason = "the failing method decides the contract error, so the error carries no value"
132 )]
133 fn ensure(args: &Self::Args, reply: &Self::Reply) -> Result<(), ()>;
134}
135
136/// One member of an interface.
137#[derive(Clone, Copy, Debug, PartialEq, Eq)]
138pub struct Member {
139 /// The member's ordinal.
140 pub ordinal: Ordinal,
141 /// The member's kind.
142 pub kind: Kind,
143 /// The member's name.
144 pub name: &'static str,
145 /// The member's timing, as the IR resolved it. `None` when the IR carries
146 /// no timing: a `command` or a `query` with no timing annotation, or a
147 /// `fixed`.
148 pub timing: Option<Timing>,
149 /// One entry per payload: two for a `query` (the request, then the
150 /// reply), one for every other kind.
151 pub payloads: &'static [PayloadInfo],
152}
153
154/// The form of a timing annotation (ridl §9).
155#[derive(Clone, Copy, Debug, PartialEq, Eq)]
156pub enum TimingMode {
157 /// `@Xms`: a strict period, on a signal only (ridl §9.2).
158 StrictPeriodic,
159 /// `@[min..max]`, where either side may be absent.
160 Range,
161}
162
163/// A member's timing (ridl §9).
164///
165/// `max` is the staleness bound of a signal, the time to live of an event, and
166/// the response bound of a call.
167///
168/// Under `StrictPeriodic`, `min` and `max` both hold the period.
169#[derive(Clone, Copy, Debug, PartialEq, Eq)]
170pub struct Timing {
171 /// The form of the annotation.
172 pub mode: TimingMode,
173 /// The lower bound. `None` when the IR leaves it unset.
174 pub min: Option<Duration>,
175 /// The upper bound. `None` when the IR leaves it unset.
176 pub max: Option<Duration>,
177}
178
179/// One payload of a member.
180#[derive(Clone, Copy, Debug, PartialEq, Eq)]
181pub struct PayloadInfo {
182 /// The payload type's name.
183 pub type_name: &'static str,
184 /// The payload's largest encoded size in each core encoding.
185 pub max_size: EncodedSizes,
186}
187
188/// A payload's largest encoded size in bytes, one field per core encoding.
189///
190/// A field is `None` when the toolchain cannot size the payload for that
191/// encoding. That covers two cases the reader does not have to tell apart: the
192/// encoding cannot carry the payload at all, and the encoding can carry it but
193/// the size is not derivable yet, because the story that defines the layout has
194/// not landed. The `repr(C)` column is `None` for every payload until E11.12
195/// defines the C-representable layout, and a backend that emits descriptors
196/// before its codec exists writes `None` for that codec's column too.
197///
198/// So a consumer reads `None` as "no size is available here", never as "this
199/// payload cannot be encoded this way", and asks the catalog descriptor rather
200/// than this field when it needs to know which encodings a payload has.
201#[derive(Clone, Copy, Debug, PartialEq, Eq)]
202pub struct EncodedSizes {
203 /// The proto3 size.
204 pub proto3: Option<u32>,
205 /// The FlatBuffers size.
206 pub flatbuffers: Option<u32>,
207 /// The `repr(C)` size.
208 pub repr_c: Option<u32>,
209}
210
211#[cfg(test)]
212mod tests {
213 use super::{CatalogHash, CatalogRef, Kind};
214
215 #[test]
216 fn catalog_refs_are_equal_only_when_name_and_hash_are_equal() {
217 let base = CatalogRef {
218 name: "vehicle",
219 hash: CatalogHash([1; 32]),
220 };
221 let same = CatalogRef {
222 name: "vehicle",
223 hash: CatalogHash([1; 32]),
224 };
225 let other_hash = CatalogRef {
226 name: "vehicle",
227 hash: CatalogHash([2; 32]),
228 };
229 let other_name = CatalogRef {
230 name: "cabin",
231 hash: CatalogHash([1; 32]),
232 };
233 assert_eq!(base, same);
234 assert_ne!(base, other_hash);
235 assert_ne!(base, other_name);
236 }
237
238 #[test]
239 fn kind_values_are_the_language_order_from_one() {
240 assert_eq!(Kind::Signal as u8, 1);
241 assert_eq!(Kind::Event as u8, 2);
242 assert_eq!(Kind::Command as u8, 3);
243 assert_eq!(Kind::Query as u8, 4);
244 assert_eq!(Kind::Fixed as u8, 5);
245 }
246}