Skip to main content

Crate phoxal_api

Crate phoxal_api 

Source
Expand description

The single API layer (D60/D61/D1).

This crate is the versioned API contract tree. It depends only on the phoxal-bus ABI floor (the contract primitive traits and the typed-topic builders) and the phoxal-macros proc-macros; it does not depend on the phoxal engine. Normal participants import the train-selected facade with use phoxal::api; concrete modules such as phoxal_api::v0_1 remain available to compatibility adapters.

§Concrete API revisions

An API revision is a conventional vM_N module generated by phoxal_api_tree!. Each version module carries:

  • a zero-variant marker enum Api {} implementing ApiVersion, whose ApiVersion::ID is the concrete wire identity (for example "v0.1");
  • the version-local wire bodies, one pub mod per contract node holding plain serde structs/enums and their ContractBody impls;
  • an api-local topic builder rooted at topic::client().

From 1.0, published concrete revisions are immutable. Before 1.0 the framework may make an approved in-place breaking edit without adding a shim or a new revision; every participant on a robot must move as one train because mixed pre-1.0 framework trains are unsupported. A child may extend one earlier revision; the generator materializes the complete child tree with its own identity. Exactly one latest alias is selected for each framework train.

§Train-selected revision and per-contract identity

A participant creates typed bus handles during Participant::setup. Official participants name contract types through the complete train-selected facade. Embedded participant metadata carries {id, config_schema}. Across the graph, compatibility is name identity (D1) - two participants interoperate on a contract iff they use the exact same version-qualified name (v0.1::drive::Target), which is real on the wire because the revision is folded into the key (ContractBody::TOPIC). From 1.0 onward a stable contract type is immutable, so the name is the whole identity. Before 1.0, that identity is train-scoped and an in-place edit requires the whole robot graph to upgrade together.

§Plain serde wire bodies, provenance in metadata

A wire body is just its serde encoding - there is no {"v":…} envelope or any other version tag inside the payload (D62). Identity lives entirely in the Zenoh key (the version-qualified ContractBody::TOPIC); the bus metadata alongside the encoded body carries only provenance (source + logical time) and the codec that produced the bytes - never schema/family/version. Keeping identity out of both the payload and the metadata means the body bytes for an unchanged contract are identical across codecs, and a receiver’s per-key subscription is the whole fast-reject.

§Topic

ContractBody::TOPIC is derived from the contract node’s path in the tree, never written by hand: the version, then the /-joined node path plus the topic leaf, with each dynamic node contributing a {var} placeholder, e.g. v0.1/component/{instance}/motor/{capability}/command. A fully static path has a literal key (v0.1/drive/state). Folding the revision into the key (D1) is what makes two differently-versioned contracts physically distinct Zenoh keys, so they cannot collide.

§The api-local topic builder

Each version module exposes a topic builder that mirrors the node tree: api::topic::client() returns a root, one method per top-level node walks down the tree, a dynamic node’s method takes its variable as impl Display, and a leaf method binds the topic’s side-branded kind to its version-local body. For example api::topic::client().drive().state() yields a Topic<Subscribe<drive::State>> (the CLIENT observes the owner’s state) over the version-qualified key v0.1/drive/state, and api::topic::client().component("base").motor("left").command() fills the dynamic segments to produce v0.1/component/base/motor/left/command. Because the builder is generated from the same tree as TOPIC, the built key and the documented key stay in lockstep.

§Owner side: topic::owner

The PUBLIC topic::client()... chain above is the client side. The matching owner side lives at api::topic::owner()...: the same node tree and keys, but the leaf brands flip so the owner gets the side it must take - api::topic::owner().drive().state() is Topic<Publish<drive::State>> (the owner publishes its telemetry), and api::topic::owner().drive().target() is Topic<Subscribe<drive::Target>> (the owner reads its command input). A query owner reaches its ServeQuery brand the same way. The owner chain makes that ownership explicit; a participant acquires the topics of its OWN node through it and everything it consumes through the client chain.

Re-exports§

pub use v0_1 as latest;

Modules§

v0_1
Concrete API revision v0.1 - version-local wire bodies + topics.

Traits§

ApiVersion
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.
ContractBody
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.