pub trait ContractBody:
Serialize
+ DeserializeOwned
+ Clone
+ Send
+ Sync
+ 'static {
type Api: ApiVersion;
const NAME: &'static str;
const GENERATION: &'static str;
const CONTRACT: &'static str;
const TOPIC: &'static str;
}Expand description
The contract primitive traits, re-exported from the phoxal-bus crate (the
ABI floor) so they stay addressable at phoxal_api::ApiVersion /
phoxal_api::ContractBody.
ApiVersionis the marker trait identifying one dated API version (D60), implemented only by the zero-variantenum Api {}thatphoxal_api_tree!generates inside each version module; itsIDis the dated module name ("y2026_1"). ItsIS_PREVIEWconst is lifecycle metadata only.ContractBodyis a version-local wire body (D61): a plain serde type bound to exactly oneApiVersionand one contract topic. Every body declared inside aphoxal_api_tree!node gets a generated impl; handles,SetupContextbuilders, and theService/Driverderive assertions key off itsApi/TOPIC.TOPICis generation-qualified (D1) and is the compatibility key - there is noSCHEMA_ID/FAMILY; its serde encoding is the wire payload, with no version envelope (D62). A version-local wire body: a plain serde type bound to exactly oneApiVersionand one contract topic (D61/D1).
Every body declared inside a phoxal_api_tree! node gets a generated impl.
Each body carries its own Api generation marker and
generation-qualified 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 generation is folded into
TOPIC, so y2026_1::drive::Target and a
hypothetically re-minted y2026_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.
Required Associated Constants§
Sourceconst NAME: &'static str
const NAME: &'static str
The version-qualified type path this body’s own generation module
places it at: the version module name, then the ::-joined node path
(dynamic-node vars are topic params, never type-path segments), then
the PascalCase type leaf, e.g. "y2026_1::drive::Target" or
"y2026_1::component::motor::Command". This is the contract’s source
identity (D1) - two contracts interoperate iff they share this exact
name - as distinct from TOPIC, the resolved
wire key derived from it. NAME is exactly the "::"-join of
GENERATION and
CONTRACT; it stays available for callers
that want the whole identity as one string (e.g. display), while
metadata recording splices the two split consts instead (coherence-gate
design doc §2 - a joined name is not machine-parseable without
assuming the generation naming scheme).
Sourceconst GENERATION: &'static str
const GENERATION: &'static str
This body’s generation alone, e.g. "y2026_1" - equal to
<Self::Api as ApiVersion>::ID, but exposed directly on the body so a
metadata recorder (#[derive(phoxal::Api)]’s linker-section
splicing) can const-splice it without routing through Self::Api.
Split from CONTRACT so consumers (the
coherence gate, catalog) record generation and contract as two
separate fields rather than parsing a joined name.
Sourceconst CONTRACT: &'static str
const CONTRACT: &'static str
This body’s contract path within its own generation: the ::-joined
node path (dynamic-node vars excluded, as with NAME) plus the
PascalCase type leaf, e.g. "drive::Target". The logical
contract - stable across a generation bump - is this value alone;
pairing it with GENERATION recovers the
full version-qualified identity (NAME).
Sourceconst TOPIC: &'static str
const TOPIC: &'static str
The generation-qualified wire key: the version module name, then the
/-joined node path plus the topic leaf, with each dynamic node
contributing a {var} placeholder, e.g. "y2026_1/drive/state" or
"y2026_1/component/{instance}/motor/{capability}/command". The concrete
key is produced by the api-local topic builder, which fills the
placeholders.
Required Associated Types§
Sourcetype Api: ApiVersion
type Api: ApiVersion
The single API version this body belongs to. Two bodies from different
versions have different Api, so the type system keeps them apart.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".