1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//! The contract primitive traits (D60/D61): the API-version marker and the
//! version-local wire body.
//!
//! These are the two traits the bus client is generic over - the ABI floor every
//! contract body and api-version marker implements. The concrete versioned API
//! versions (`phoxal::api`, …) and the `phoxal_api_tree!` macro that
//! generates their `ApiVersion` / `ContractBody` impls live in the `phoxal-api`
//! crate, which re-exports these traits - so they are reachable as
//! `phoxal_api::ApiVersion` / `phoxal_api::ContractBody`. The `phoxal` engine
//! re-exports this bus crate at `phoxal::bus`, so they are also reachable as
//! `phoxal::bus::ApiVersion` / `phoxal::bus::ContractBody`.
/// Marker trait identifying one API version (D60).
///
/// Implemented only by the zero-variant `enum Api {}` that
/// [`phoxal_api_tree!`] generates inside each revision module. The [`ID`] is the
/// dotted wire revision (`"v0.1"`) and is carried in bus metadata as
/// informational provenance, never in the wire body or the topic key (D62).
///
/// [`ID`]: ApiVersion::ID
/// [`phoxal_api_tree!`]: https://docs.rs/phoxal
/// The semantic **and temporal** role a topic plays in its owning service's
/// contract.
///
/// Every topic in a [`phoxal_api_tree!`] declares one of these (D63, plan #00).
/// The role records *intent*, separate from the wire shape: a `Command` and a
/// `State` topic are both pub/sub on the wire, but the owner subscribes a
/// `Command` (it is the service's control input) and publishes a `State` (it is
/// the service's telemetry output).
///
/// The role drives two things:
///
/// - the **side branding** (L1): the api tree's builders read it to pick each
/// leaf's side-branded topic kind
/// (`Publish`/`Subscribe`/`AskQuery`/`ServeQuery`), so taking the wrong side
/// of a topic is a compile error;
/// - the **temporal capability** (#952 section D): the role decides which robot
/// time a publisher of that contract can express at all. A `State` is
/// published at a logical step, a `Measurement` carries a capture stamp, and
/// a `Command` or `Diagnostic` expresses no robot time. The generated body
/// implements exactly one of [`StateContract`] / [`MeasurementContract`] /
/// [`CommandContract`] / [`DiagnosticContract`], and each publisher handle is
/// bounded by its own marker, so reaching for the wrong publisher is a
/// compile error rather than a review question. One topic is the sole
/// exception: the framework's own `world_clock`-role `simulation::Clock`
/// wire-brands and reports `TopicRole::State` exactly like an ordinary state
/// topic, but implements the disjoint [`WorldClockContract`] instead of
/// `StateContract` - see that trait's docs for why.
///
/// [`phoxal_api_tree!`]: https://docs.rs/phoxal
/// Marker for a body whose topic role is [`TopicRole::State`].
///
/// Generated by `phoxal_api_tree!` for every ordinary `state` topic. Deliberately
/// NOT implemented for the framework's own world-clock contract
/// (`phoxal::api::simulation::Clock`) - see [`WorldClockContract`] for why that
/// exclusion is the enforcement mechanism, not an oversight.
/// Marker for the framework's single world-clock contract
/// (`phoxal::api::simulation::Clock`, generated by `phoxal_api_tree!`'s
/// `world_clock` topic role).
///
/// Deliberately a SIBLING of [`StateContract`], not a subtrait of it: if the
/// world clock also implemented `StateContract`, it would still satisfy the
/// ordinary, unrestricted `state_publisher` builder every participant has,
/// which is exactly the unenforced convention organization#957 found - "only
/// a simulator can mint world steps" was a doc comment, not a compiler rule.
/// Excluding it from `StateContract` is what makes that builder reject it at
/// compile time, forcing every caller through the world-authority-gated
/// `SetupContextSimulatorExt::world_clock_publisher` in the `phoxal` crate
/// instead (`Self: IsSimulator`).
///
/// Bounds [`WorldClockPublisher`](crate::handle::WorldClockPublisher), a
/// dedicated handle type separate from
/// [`StatePublisher`](crate::handle::StatePublisher) even though both publish
/// at a logical step with the same [`StepStamp`](crate::handle::StepStamp)
/// path: sharing one generic handle type across both traits would force
/// `StatePublisher`'s bound onto a common supertrait, which would blur an
/// ordinary participant's "wrong contract for `StatePublisher`" compile error
/// (today a precise `B: StateContract` message with the real `state` topics
/// listed as candidates) into a less legible one naming an internal plumbing
/// trait instead. Two small handle types keep that diagnostic exact.
/// Marker for a body whose topic role is [`TopicRole::Measurement`].
///
/// Generated by `phoxal_api_tree!`; it is the bound on
/// [`MeasurementPublisher`](crate::handle::MeasurementPublisher), whose publish
/// path requires a capture stamp the driver derived from its device clock.
/// Marker for a body whose topic role is [`TopicRole::Command`].
///
/// Generated by `phoxal_api_tree!`; it is the bound on
/// [`CommandPublisher`](crate::handle::CommandPublisher), which expresses no
/// robot time at all.
/// Marker for a body whose topic role is [`TopicRole::Diagnostic`].
///
/// Generated by `phoxal_api_tree!`; it is the bound on
/// [`DiagnosticPublisher`](crate::handle::DiagnosticPublisher), which expresses
/// no robot time at all.
/// A version-local wire body: a plain serde type bound to exactly one
/// [`ApiVersion`] and one contract topic (D61/D1).
///
/// Every body declared inside a `phoxal_api_tree!` node gets a generated impl.
/// Each body carries its own [`Api`](ContractBody::Api) version marker and
/// version-qualified [`TOPIC`](ContractBody::TOPIC).
/// Participant `Api` derives record contract bodies field by field, and setup
/// builders require the requested handle body to be declared by that struct.
///
/// The serde encoding of an implementor *is* the wire payload; there is no version
/// envelope (D62).
///
/// **Wire identity is the key, not a hash (D1).** The version is folded into
/// [`TOPIC`](ContractBody::TOPIC), so `v0.1::drive::Target` and a
/// hypothetically re-minted `v0.2::drive::Target` publish on different Zenoh
/// keys and physically cannot collide. There is therefore no `SCHEMA_ID`/`FAMILY`
/// axis: two participants interoperate on a contract iff they use the exact same
/// version-qualified name, which is realized on the wire by the key.
/// A receiver's per-key Zenoh subscription is the
/// whole fast-reject; the bus decode path validates only the codec.