use std::marker::PhantomData;
use crate::bus::contract::{Endpoint, EndpointSemantics};
use crate::bus::topic::{KeySegment, KeySegmentError, Topic};
pub struct BoundEndpoint<E: Endpoint> {
key: String,
_endpoint: PhantomData<fn() -> E>,
}
impl<E: Endpoint> BoundEndpoint<E> {
pub(crate) fn new(key: String) -> Self {
BoundEndpoint {
key,
_endpoint: PhantomData,
}
}
#[must_use]
pub fn key(&self) -> &str {
&self.key
}
#[must_use]
pub fn client(self) -> Topic<<E::Semantics as EndpointSemantics>::Client<E>> {
Topic::new(self.key)
}
#[must_use]
pub fn owner(self) -> Topic<<E::Semantics as EndpointSemantics>::Owner<E>> {
Topic::new(self.key)
}
}
pub trait TopicSegment {
fn segment(&self) -> Result<KeySegment, KeySegmentError>;
}
impl TopicSegment for KeySegment {
fn segment(&self) -> Result<KeySegment, KeySegmentError> {
Ok(self.clone())
}
}
macro_rules! nodes {
(
family $family:ident ;
$( $node:ident $( ( $variable:ident : $segment:ty ) )? ; )+
) => {
pub(crate) type Family = crate::bus::$family;
crate::bus::tree::path_node!();
impl Path {
fn __root() -> Self {
Path {
key: ::std::string::ToString::to_string(
<Family as crate::bus::Family>::ID,
),
}
}
}
#[doc = ::core::concat!("Begin a path through the `", ::core::stringify!($family), "` family, rooted at its own leading key segment.")]
#[must_use]
pub fn topics() -> Path {
Path::__root()
}
$( pub mod $node; )+
$( crate::bus::tree::node_child!( $node $( ( $variable : $segment ) )? ); )+
pub(crate) fn contract_records(
out: &mut ::std::vec::Vec<crate::__compat::surface::ContractRecord>,
) {
let root = <Family as crate::bus::Family>::ID;
$( crate::bus::tree::node_record!(root, out, $node $( ( $variable : $segment ) )? ); )+
}
};
( $( $node:ident $( ( $variable:ident : $segment:ty ) )? ; )+ ) => {
pub(crate) use super::Family;
crate::bus::tree::path_node!();
crate::bus::tree::path_child!();
$( pub mod $node; )+
$( crate::bus::tree::node_child!( $node $( ( $variable : $segment ) )? ); )+
pub(crate) fn contract_records(
prefix: &str,
out: &mut ::std::vec::Vec<crate::__compat::surface::ContractRecord>,
) {
$( crate::bus::tree::node_record!(prefix, out, $node $( ( $variable : $segment ) )? ); )+
}
};
}
macro_rules! endpoints {
( $( $leaf:tt : $semantics:tt < $( $body:tt ),+ $(,)? > ; )+ ) => {
pub(crate) use super::Family;
crate::bus::tree::path_node!();
crate::bus::tree::path_child!();
$( crate::bus::tree::endpoint!( $leaf, $semantics, $( $body ),+ ); )+
pub(crate) fn contract_records(
prefix: &str,
out: &mut ::std::vec::Vec<crate::__compat::surface::ContractRecord>,
) {
$( crate::bus::tree::endpoint_record!(prefix, out, $leaf, $semantics, $( $body ),+ ); )+
}
};
}
macro_rules! path_node {
() => {
pub struct Path {
key: ::std::string::String,
}
};
}
macro_rules! path_child {
() => {
impl Path {
pub(crate) fn __child(parent: &str, segment: &str) -> Self {
Path {
key: ::std::format!("{parent}/{segment}"),
}
}
}
};
}
macro_rules! node_child {
( $node:ident ) => {
impl Path {
#[doc = ::core::concat!("Descend into the `", ::core::stringify!($node), "` node.")]
#[must_use]
pub fn $node(&self) -> $node::Path {
$node::Path::__child(&self.key, ::core::stringify!($node))
}
}
};
( $node:ident ( $variable:ident : $segment:ty ) ) => {
impl Path {
#[doc = ::core::concat!("Descend into the `", ::core::stringify!($node), "/{", ::core::stringify!($variable), "}` node, binding `", ::core::stringify!($variable), "` as its dynamic segment.")]
///
/// # Errors
///
/// Returns [`KeySegmentError`](crate::bus::KeySegmentError) when the
/// bound value is not one concrete key segment.
pub fn $node(
&self,
$variable: &$segment,
) -> ::core::result::Result<$node::Path, crate::bus::KeySegmentError> {
let bound = crate::bus::TopicSegment::segment($variable)?;
::core::result::Result::Ok($node::Path::__child(
&self.key,
&::std::format!("{}/{}", ::core::stringify!($node), bound),
))
}
}
};
}
/// Recurse the compatibility aggregation into one child node, spelling a
/// dynamic segment as its `{variable}` template.
macro_rules! node_record {
( $prefix:expr, $out:expr, $node:ident ) => {
$node::contract_records(
&::std::format!("{}/{}", $prefix, ::core::stringify!($node)),
$out,
);
};
( $prefix:expr, $out:expr, $node:ident ( $variable:ident : $segment:ty ) ) => {
$node::contract_records(
&::std::format!(
"{}/{}/{{{}}}",
$prefix,
::core::stringify!($node),
::core::stringify!($variable)
),
$out,
);
};
}
/// One endpoint declaration: map the authored semantic to its semantics type,
/// and add the query response linkage where there is one.
macro_rules! endpoint {
( $leaf:tt, State, $body:tt ) => {
crate::bus::tree::endpoint_declare!($leaf, crate::bus::State, $body);
};
( $leaf:tt, Sample, $body:tt ) => {
crate::bus::tree::endpoint_declare!($leaf, crate::bus::Sample, $body);
};
( $leaf:tt, Event, $body:tt ) => {
crate::bus::tree::endpoint_declare!($leaf, crate::bus::Event, $body);
};
( $leaf:tt, Setpoint, $body:tt ) => {
crate::bus::tree::endpoint_declare!($leaf, crate::bus::Setpoint, $body);
};
( $leaf:tt, WorldClock, $body:tt ) => {
crate::bus::tree::endpoint_declare!($leaf, crate::bus::WorldClock, $body);
};
( $leaf:tt, Stream, $body:tt, In ) => {
crate::bus::tree::endpoint_declare!($leaf, crate::bus::Stream<crate::bus::In>, $body);
};
( $leaf:tt, Stream, $body:tt, Out ) => {
crate::bus::tree::endpoint_declare!($leaf, crate::bus::Stream<crate::bus::Out>, $body);
};
( $leaf:tt, Query, $request:tt, $response:tt ) => {
crate::bus::tree::endpoint_declare!($leaf, crate::bus::Query, $request);
impl crate::bus::QueryEndpoint for $request {
type Response = $response;
}
};
}
/// The sealed endpoint typing plus the builder that binds this leaf.
macro_rules! endpoint_declare {
( self, $semantics:ty, $body:tt ) => {
crate::bus::tree::endpoint_bind!($semantics, $body);
impl Path {
/// Take the external client's side of this node's own endpoint.
#[must_use]
pub fn client(
self,
) -> crate::bus::Topic<<$semantics as crate::bus::EndpointSemantics>::Client<$body>>
{
crate::bus::BoundEndpoint::<$body>::new(self.key).client()
}
/// Take the endpoint owner's side of this node's own endpoint.
#[must_use]
pub fn owner(
self,
) -> crate::bus::Topic<<$semantics as crate::bus::EndpointSemantics>::Owner<$body>>
{
crate::bus::BoundEndpoint::<$body>::new(self.key).owner()
}
}
};
( $leaf:ident, $semantics:ty, $body:tt ) => {
crate::bus::tree::endpoint_bind!($semantics, $body);
impl Path {
#[doc = ::core::concat!("Bind the `", ::core::stringify!($leaf), "` endpoint of this node.")]
#[must_use]
pub fn $leaf(&self) -> crate::bus::BoundEndpoint<$body> {
crate::bus::BoundEndpoint::new(::std::format!(
"{}/{}",
self.key,
::core::stringify!($leaf)
))
}
}
};
}
/// Attach the family and the semantics to the payload type, sealing it as an
/// endpoint.
macro_rules! endpoint_bind {
( $semantics:ty, $body:tt ) => {
impl crate::bus::contract::sealed::Endpoint for $body {}
impl crate::bus::Endpoint for $body {
type Family = self::Family;
type Semantics = $semantics;
}
};
}
/// The key template one endpoint contributes below `prefix`.
macro_rules! endpoint_key {
( $prefix:expr, self ) => {
::std::string::String::from($prefix)
};
( $prefix:expr, $leaf:ident ) => {
::std::format!("{}/{}", $prefix, ::core::stringify!($leaf))
};
}
/// One endpoint's compatibility record.
macro_rules! endpoint_record {
( $prefix:expr, $out:expr, $leaf:tt, Stream, $body:tt, $direction:tt ) => {
crate::bus::tree::endpoint_topic_record!(
$prefix,
$out,
$leaf,
crate::bus::Stream<crate::bus::$direction>,
$body
);
};
( $prefix:expr, $out:expr, $leaf:tt, Query, $request:tt, $response:tt ) => {
$out.push(crate::__compat::surface::ContractRecord::query(
<self::Family as crate::bus::Family>::ID,
crate::bus::tree::endpoint_key!($prefix, $leaf),
<crate::bus::Query as crate::bus::EndpointSemantics>::KIND.as_str(),
<crate::bus::Query as crate::bus::EndpointSemantics>::DELIVERY.as_str(),
<$request as crate::__compat::wire::DescribeWire>::wire_schema(),
<$response as crate::__compat::wire::DescribeWire>::wire_schema(),
));
};
( $prefix:expr, $out:expr, $leaf:tt, $semantics:tt, $body:tt ) => {
crate::bus::tree::endpoint_topic_record!(
$prefix,
$out,
$leaf,
crate::bus::$semantics,
$body
);
};
}
/// One pub/sub endpoint's compatibility record.
macro_rules! endpoint_topic_record {
( $prefix:expr, $out:expr, $leaf:tt, $semantics:ty, $body:tt ) => {
$out.push(crate::__compat::surface::ContractRecord::topic(
<self::Family as crate::bus::Family>::ID,
crate::bus::tree::endpoint_key!($prefix, $leaf),
<$semantics as crate::bus::EndpointSemantics>::KIND.as_str(),
<$semantics as crate::bus::EndpointSemantics>::DELIVERY.as_str(),
<$body as crate::__compat::wire::DescribeWire>::wire_schema(),
));
};
}
pub(crate) use endpoint;
pub(crate) use endpoint_bind;
pub(crate) use endpoint_declare;
pub(crate) use endpoint_key;
pub(crate) use endpoint_record;
pub(crate) use endpoint_topic_record;
pub(crate) use endpoints;
pub(crate) use node_child;
pub(crate) use node_record;
pub(crate) use nodes;
pub(crate) use path_child;
pub(crate) use path_node;