phoxal-bus 0.45.3

Phoxal bus ABI floor: the Zenoh-native contract bus client and the API-version / contract-body primitive traits.
Documentation
//! # phoxal-bus
//!
//! The Phoxal bus ABI floor: the Zenoh-native wire boundary (D26/D62/D1) plus
//! the two contract primitive traits ([`ApiVersion`] / [`ContractBody`]) the bus
//! client is generic over.
//!
//! Samples are Zenoh-native: the key
//! (`<namespace>/robots/<robot-id>/<version>/<topic>`, the version folded
//! in per D1), an encoding string + a [`BusMetadata`] attachment (codec and
//! provenance only - no schema/family/api identity, D1), and a plain MessagePack
//! body payload - there is no Phoxal frame independent of Zenoh, and no
//! `{"v":…}` version tag in the body (D62). Identity lives entirely in the key:
//! different version-qualified contract names are different keys and physically
//! cannot collide, so a receiver's per-key subscription is the whole fast-reject.
//!
//! The concrete API versions (`v1`, …) generated by the
//! `phoxal_api_tree!` macro - and their [`ApiVersion`] / [`ContractBody`] impls -
//! live in the `phoxal-api` crate (`phoxal::api`, …), which also re-exports
//! these two traits. The `phoxal` engine re-exports the typed authoring subset at
//! `phoxal::bus`, so the traits are also reachable as `phoxal::bus::ApiVersion` /
//! `phoxal::bus::ContractBody`. The raw session-opening surface lives behind the
//! explicit `phoxal-bus` opt-in.

pub mod abi;
pub mod codec;
pub mod contract;
pub mod error;
pub mod handle;
pub mod identity;
pub mod lease;
pub mod liveliness;
pub mod metadata;
pub mod query;
mod runtime_metrics;
pub mod server;
pub mod session;
pub mod time;
pub mod topic;

pub use abi::{CodecId, encoding_string, parse_encoding_string};
pub use codec::{Codec, CodecError, MessagePack};
pub use contract::{
    ApiVersion, CommandContract, ContractBody, DiagnosticContract, MeasurementContract,
    StateContract, TopicRole, WorldClockContract,
};
pub use error::{BusError, Result};
pub use handle::{
    CommandPublisher, DEFAULT_QUERY_TIMEOUT, DiagnosticPublisher, Latest, MeasurementPublisher,
    Observed, Querier, StatePublisher, StepStamp, StepToken, Subscriber, TimelineAuthority,
    WorldClockPublisher, WorldStepToken,
};
pub use identity::{ExecutionId, InvalidIdentity, ProducerId, TimelineId};
pub use lease::{LEASE_TRACE_TARGET, Lease, LeaseDecision, LeaseRejection, ProducerFence};
pub use liveliness::{
    ParticipantLivelinessEvent, ParticipantLivelinessKey, ParticipantLivelinessObserver,
    ParticipantLivelinessStatus, ParticipantLivelinessToken,
};
pub use metadata::BusMetadata;
pub use query::{QueryCode, QueryError, QueryFailure, QueryResult};
#[doc(hidden)]
pub use runtime_metrics::{
    RuntimeBufferKind, RuntimeDirection, RuntimeMetricKey, RuntimeMetricSnapshot,
};
pub use server::{IncomingQuery, ServerQueryable};
pub use session::{Bus, BusConfig, BusHealth};
pub use time::{
    CaptureStamp, LocalInstant, RobotInstant, TimeWindow, TimelineMismatch, WallTimestamp,
};
pub use topic::{AskQuery, Publish, ServeQuery, Subscribe, Topic, TopicKind, WildcardPublish};

use std::collections::HashSet;

/// The world histories this process has seen replaced.
///
/// Retirement is permanent for the life of the process. It used to be a
/// bounded ring, which was wrong in the way that matters: timelines are
/// equality-only identities with no ordering, so once an identity is evicted
/// nothing can recognise it as old. A delayed clock from the ninth-oldest
/// controller would then read as a *new* world and reset every participant back
/// into a history that had already ended.
///
/// The memory is one identity per world replacement - a reset the operator
/// asked for - so it grows with operator actions, not with traffic.
#[doc(hidden)]
#[derive(Debug, Default)]
pub struct RetiredTimelines {
    timelines: HashSet<TimelineId>,
}

impl RetiredTimelines {
    pub fn contains(&self, timeline: TimelineId) -> bool {
        self.timelines.contains(&timeline)
    }

    pub fn retire(&mut self, timeline: TimelineId) {
        self.timelines.insert(timeline);
    }

    pub fn activate(&mut self, timeline: TimelineId) {
        self.timelines.remove(&timeline);
    }
}

#[cfg(test)]
mod tests;