use std::marker::PhantomData;
use crate::bus::topic::{AskQuery, Publish, ServeQuery, Subscribe, TopicKind};
pub(crate) mod sealed {
pub trait Endpoint {}
pub trait Semantics {}
pub trait Family {}
pub trait Direction {}
}
pub trait Payload: serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static {}
impl<T> Payload for T where T: serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static
{}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DeliveryFamily {
State,
Sample,
Setpoint,
Stream,
Query,
}
impl DeliveryFamily {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::State => "state",
Self::Sample => "sample",
Self::Setpoint => "setpoint",
Self::Stream => "stream",
Self::Query => "query",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EndpointKind {
State,
Sample,
Event,
Stream,
Setpoint,
Query,
}
impl EndpointKind {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::State => "state",
Self::Sample => "sample",
Self::Event => "event",
Self::Stream => "stream",
Self::Setpoint => "setpoint",
Self::Query => "query",
}
}
#[must_use]
pub const fn delivery_family(self) -> DeliveryFamily {
match self {
Self::State => DeliveryFamily::State,
Self::Sample => DeliveryFamily::Sample,
Self::Event | Self::Stream => DeliveryFamily::Stream,
Self::Setpoint => DeliveryFamily::Setpoint,
Self::Query => DeliveryFamily::Query,
}
}
}
pub trait Family: sealed::Family + 'static {
const ID: &'static str;
}
pub enum Robot {}
pub enum Runtime {}
pub enum Supervisor {}
impl sealed::Family for Robot {}
impl Family for Robot {
const ID: &'static str = "robot";
}
impl sealed::Family for Runtime {}
impl Family for Runtime {
const ID: &'static str = "runtime";
}
impl sealed::Family for Supervisor {}
impl Family for Supervisor {
const ID: &'static str = "supervisor";
}
#[cfg(test)]
pub(crate) enum TestFamily {}
#[cfg(test)]
impl sealed::Family for TestFamily {}
#[cfg(test)]
impl Family for TestFamily {
const ID: &'static str = "yTEST";
}
pub trait Direction: sealed::Direction + 'static {}
pub enum In {}
pub enum Out {}
impl sealed::Direction for In {}
impl Direction for In {}
impl sealed::Direction for Out {}
impl Direction for Out {}
pub enum State {}
pub enum Sample {}
pub enum Event {}
pub enum Setpoint {}
pub struct Stream<D: Direction>(PhantomData<fn() -> D>);
pub enum Query {}
pub enum WorldClock {}
pub trait EndpointSemantics: sealed::Semantics + 'static {
const KIND: EndpointKind;
const DELIVERY: DeliveryFamily = Self::KIND.delivery_family();
type Client<E: Endpoint>: TopicKind;
type Owner<E: Endpoint>: TopicKind;
}
pub trait StreamDelivered: EndpointSemantics {}
impl sealed::Semantics for State {}
impl EndpointSemantics for State {
const KIND: EndpointKind = EndpointKind::State;
type Client<E: Endpoint> = Subscribe<E>;
type Owner<E: Endpoint> = Publish<E>;
}
impl sealed::Semantics for Sample {}
impl EndpointSemantics for Sample {
const KIND: EndpointKind = EndpointKind::Sample;
type Client<E: Endpoint> = Subscribe<E>;
type Owner<E: Endpoint> = Publish<E>;
}
impl sealed::Semantics for Event {}
impl EndpointSemantics for Event {
const KIND: EndpointKind = EndpointKind::Event;
type Client<E: Endpoint> = Subscribe<E>;
type Owner<E: Endpoint> = Publish<E>;
}
impl StreamDelivered for Event {}
impl sealed::Semantics for Setpoint {}
impl EndpointSemantics for Setpoint {
const KIND: EndpointKind = EndpointKind::Setpoint;
type Client<E: Endpoint> = Publish<E>;
type Owner<E: Endpoint> = Subscribe<E>;
}
impl sealed::Semantics for Stream<In> {}
impl EndpointSemantics for Stream<In> {
const KIND: EndpointKind = EndpointKind::Stream;
type Client<E: Endpoint> = Publish<E>;
type Owner<E: Endpoint> = Subscribe<E>;
}
impl StreamDelivered for Stream<In> {}
impl sealed::Semantics for Stream<Out> {}
impl EndpointSemantics for Stream<Out> {
const KIND: EndpointKind = EndpointKind::Stream;
type Client<E: Endpoint> = Subscribe<E>;
type Owner<E: Endpoint> = Publish<E>;
}
impl StreamDelivered for Stream<Out> {}
impl sealed::Semantics for Query {}
impl EndpointSemantics for Query {
const KIND: EndpointKind = EndpointKind::Query;
type Client<E: Endpoint> = AskQuery<E>;
type Owner<E: Endpoint> = ServeQuery<E>;
}
impl sealed::Semantics for WorldClock {}
impl EndpointSemantics for WorldClock {
const KIND: EndpointKind = EndpointKind::Event;
type Client<E: Endpoint> = Subscribe<E>;
type Owner<E: Endpoint> = Publish<E>;
}
impl StreamDelivered for WorldClock {}
pub trait Endpoint:
Payload + crate::__compat::wire::DescribeWire + sealed::Endpoint + 'static
{
type Family: Family;
type Semantics: EndpointSemantics;
}
pub trait QueryEndpoint: Endpoint<Semantics = Query> {
type Response: Payload + crate::__compat::wire::DescribeWire;
}
pub trait RobotEndpoint: Endpoint<Family = Robot> {}
impl<E: Endpoint<Family = Robot>> RobotEndpoint for E {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_semantic_derives_its_lane_from_its_kind() {
for (kind, delivery) in [
(
<State as EndpointSemantics>::KIND,
<State as EndpointSemantics>::DELIVERY,
),
(
<Sample as EndpointSemantics>::KIND,
<Sample as EndpointSemantics>::DELIVERY,
),
(
<Event as EndpointSemantics>::KIND,
<Event as EndpointSemantics>::DELIVERY,
),
(
<Setpoint as EndpointSemantics>::KIND,
<Setpoint as EndpointSemantics>::DELIVERY,
),
(
<Stream<In> as EndpointSemantics>::KIND,
<Stream<In> as EndpointSemantics>::DELIVERY,
),
(
<Stream<Out> as EndpointSemantics>::KIND,
<Stream<Out> as EndpointSemantics>::DELIVERY,
),
(
<Query as EndpointSemantics>::KIND,
<Query as EndpointSemantics>::DELIVERY,
),
(
<WorldClock as EndpointSemantics>::KIND,
<WorldClock as EndpointSemantics>::DELIVERY,
),
] {
assert_eq!(kind.delivery_family(), delivery);
}
}
#[test]
fn the_world_clock_keeps_the_event_wire_kind() {
assert_eq!(
<WorldClock as EndpointSemantics>::KIND,
<Event as EndpointSemantics>::KIND
);
assert_eq!(
<WorldClock as EndpointSemantics>::DELIVERY,
DeliveryFamily::Stream
);
}
#[test]
fn every_family_identifies_itself_by_name() {
assert_eq!(<Robot as Family>::ID, "robot");
assert_eq!(<Runtime as Family>::ID, "runtime");
assert_eq!(<Supervisor as Family>::ID, "supervisor");
}
}