phoxal-bus 0.40.2

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::raw` opt-in.

pub mod abi;
pub mod capability;
pub mod codec;
pub mod contract;
pub mod error;
pub mod handle;
pub mod liveliness;
pub mod metadata;
pub mod query;
mod runtime_metrics;
pub mod server;
pub mod session;
pub mod topic;

pub use abi::{CodecId, encoding_string, parse_encoding_string};
pub use capability::OwnerCap;
pub use codec::{Codec, CodecError, MessagePack};
pub use contract::{ApiVersion, ContractBody, TopicRole};
pub use error::{BusError, Result};
pub use handle::{DEFAULT_QUERY_TIMEOUT, Latest, Publisher, Querier, Received, Subscriber};
pub use liveliness::{
    ParticipantLivelinessEvent, ParticipantLivelinessKey, ParticipantLivelinessObserver,
    ParticipantLivelinessStatus, ParticipantLivelinessToken,
};
pub use metadata::{BusMetadata, Source};
pub use query::{QueryCode, QueryError, QueryFailure, ServerResult};
#[doc(hidden)]
pub use runtime_metrics::{
    RuntimeBufferKind, RuntimeDirection, RuntimeMetricKey, RuntimeMetricSnapshot,
};
pub use server::{IncomingQuery, ServerQueryable};
pub use session::{Bus, BusConfig, BusHealth};
pub use topic::{AskQuery, Publish, ServeQuery, Subscribe, Topic, TopicKind, WildcardPublish};

use std::collections::VecDeque;

/// Fixed history used wherever opaque simulation epochs are retired.
///
/// The bounded history rejects in-flight samples from recently replaced
/// executions without imposing numeric ordering or unbounded memory growth.
#[doc(hidden)]
#[derive(Debug, Default)]
pub struct RetiredEpochs {
    epochs: VecDeque<u64>,
}

impl RetiredEpochs {
    /// The shared retirement-history bound for clock and data paths.
    pub const CAPACITY: usize = 8;

    pub fn contains(&self, epoch: u64) -> bool {
        self.epochs.contains(&epoch)
    }

    pub fn retire(&mut self, epoch: u64) {
        if epoch == 0 {
            return;
        }
        self.epochs.retain(|candidate| *candidate != epoch);
        if self.epochs.len() == Self::CAPACITY {
            self.epochs.pop_front();
        }
        self.epochs.push_back(epoch);
    }

    pub fn activate(&mut self, epoch: u64) {
        self.epochs.retain(|candidate| *candidate != epoch);
    }
}

/// Logical robot time: an epoch + a nanosecond timestamp in the clock's domain.
///
/// Every bus sample's `produced_at_ns`/`epoch` is stamped from a `LogicalTime`,
/// so all participants share one time domain (D34). Within one epoch
/// `time_ns` strictly increases. Epochs are opaque execution identities:
/// different epochs are comparable only for equality and have no numeric
/// generation order. Epoch `0` is reserved for the framework's
/// not-yet-initialized sentinel and is never a valid simulation execution.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LogicalTime {
    epoch: u64,
    time_ns: u64,
}

impl LogicalTime {
    /// Construct a logical time from an epoch + nanosecond timestamp.
    pub const fn new(epoch: u64, time_ns: u64) -> Self {
        LogicalTime { epoch, time_ns }
    }

    /// The opaque simulation execution identity.
    pub const fn epoch(self) -> u64 {
        self.epoch
    }

    /// The nanosecond timestamp within the epoch.
    pub const fn time_ns(self) -> u64 {
        self.time_ns
    }
}

#[cfg(test)]
mod tests;