pub struct Slot<M, W, E, Pipe = PublishIdentity, Body = ()> { /* private fields */ }Expand description
One arena entry: the wired live value of the marker M, plus the publish path the include
site gave it - its encode codec and the pipeline every message leaving the slot travels.
The wired value is the bound policy’s Live form, and the
entry is a transparent window onto it: Deref reaches every method the live value offers -
the broker capability vocabulary (TransactionalPublisher,
OwnedTransactions, RequestReply) and
any broker-defined capability trait alike, so a body pins the entry to the broker’s concrete
live type (or bounds OutEntry::Wire with the broker’s trait) and calls it directly. Under
each of the core capability bounds the entry also offers that capability’s typed form -
message, begin, transaction - over
the include site’s codec and the marker’s dictionary.
A body does not normally spell this type out: it names one parameter per marker and bounds it
with OutEntry, which projects W, E and Pipe so a mount the body never sees cannot
change its signature. Spelling the entry is for pinning it to a concrete live value.
Pipe is that publish path: the app’s own publish pipeline (the
publish_layer chain) with the slot’s
.transform(..) steps composed on top. It is PublishIdentity - nothing in the way, the
bare leaf call - until a mount site names either.
Autoderef carries a method call, not a trait bound: a helper written as fn f<L: Lanes>(l: &L) rejects the entry a body holds. A broker crate that wants such helpers - or bodies
generic over its capability - adds one blanket impl next to its trait,
impl<M, W: Lanes, E, Pipe, Body> Lanes for Slot<M, W, E, Pipe, Body>, delegating through
this Deref.
Body is the entry’s declared message set, () (any dictionary type) unless the
#[subscriber] parameter’s third Out position narrows it;
message checks it at compile time (see
ContainsMessage).
Implementations§
Source§impl<M, W, E, Pipe, Body> Slot<M, W, E, Pipe, Body>
impl<M, W, E, Pipe, Body> Slot<M, W, E, Pipe, Body>
Sourcepub fn message<'a, T, Index>(
&'a self,
value: &'a T,
) -> PublishBuilder<&'a Slot<M, W, E, Pipe, Body>, MessageBody<'a, T>, &'a E, HeadersUnset, <T as OutgoingDestination>::Form>
pub fn message<'a, T, Index>( &'a self, value: &'a T, ) -> PublishBuilder<&'a Slot<M, W, E, Pipe, Body>, MessageBody<'a, T>, &'a E, HeadersUnset, <T as OutgoingDestination>::Form>
Starts a typed publish through the slot, on the message type’s own wire
(MessageWire): a serde::Serialize value encodes with
the include site’s codec, a Serialized one carries its
bytes and they leave as they are. The
message type has to be in the marker’s #[publishes(..)] dictionary (see
PublishedThrough) and, when the entry carries a
declared message set, in that set (see
ContainsMessage); everything else - the
destination and the header contract - comes from the type’s #[derive(Outgoing)]
declaration, so the builder demands exactly the positions that declaration leaves open.
§Examples
use ruststream::runtime::{HandlerOutcome, Out};
use ruststream::{Outgoing, OutSlot, Publisher, subscriber};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct DoneMeta {
task_id: u64,
}
#[derive(Outgoing, Serialize)]
#[outgoing(name = "chunks.progress")]
struct Progress {
percent: u8,
}
#[derive(Outgoing, Serialize)]
#[outgoing(name = "chunks.done", headers = DoneMeta)]
struct ChunkDone {
output_key: String,
}
#[derive(OutSlot)]
#[publishes(ChunkDone, Progress)]
struct Events;
#[subscriber("chunks.raw")]
async fn convert(
event: &Event,
Out(out): Out<impl Publisher, Events, (ChunkDone, Progress)>,
) -> HandlerOutcome {
// No headers contract on Progress: publish straight away.
if out.message(&Progress { percent: 100 }).publish().await.is_err() {
return HandlerOutcome::retry();
}
// ChunkDone declares DoneMeta: with_headers is demanded by the contract.
let done = ChunkDone { output_key: format!("out/{}", event.id) };
let meta = DoneMeta { task_id: event.id };
if out.message(&done).with_headers(&meta).publish().await.is_err() {
return HandlerOutcome::retry();
}
HandlerOutcome::ack()
}Source§impl<M, W, E, Pipe, Body> Slot<M, W, E, Pipe, Body>
impl<M, W, E, Pipe, Body> Slot<M, W, E, Pipe, Body>
Sourcepub async fn begin(
&self,
) -> Result<TransactionScope<'_, SlotPublisher<W, M>, &E, Slot<M, W, E, Pipe, Body>>, <W as Publisher>::Error>
pub async fn begin( &self, ) -> Result<TransactionScope<'_, SlotPublisher<W, M>, &E, Slot<M, W, E, Pipe, Body>>, <W as Publisher>::Error>
Opens the entry’s broker transaction and returns the TransactionScope that owns it:
publishes go through the scope, and commit or
abort consume it, so a commit without a begin, a second
commit, or a publish after settling do not compile.
This is the borrowed kind: the wired publisher carries at most one broker-side
transaction, so one scope per entry is open at a time. The scope’s typed entry admits
what the slot’s own does (the marker’s dictionary, narrowed by the parameter’s declared
set), and publishes issued through it keep the slot’s test-capture attribution. They go
to the broker as they are: the scope opens on the attributed leaf, so the slot’s
transforms and the app-wide pipeline stay on the direct publish path (see
TransactionScope).
§Errors
Returns the publisher’s error when the broker refuses to start a transaction, or when one is already open on this handle.
§Examples
use ruststream::runtime::{HandlerOutcome, Out};
use ruststream::{Outgoing, OutSlot, TransactionalPublisher, subscriber};
use serde::Serialize;
#[derive(Outgoing, Serialize)]
#[outgoing(name = "ledger.settled")]
struct Settled {
id: u64,
}
#[derive(OutSlot)]
#[publishes(Settled)]
struct Journal;
#[subscriber("ledger.orders")]
async fn settle(
order: &Order,
Out(journal): Out<impl TransactionalPublisher, Journal, Settled>,
) -> HandlerOutcome {
let Ok(scope) = journal.begin().await else {
return HandlerOutcome::retry();
};
if scope.message(&Settled { id: order.id }).publish().await.is_err()
|| scope.commit().await.is_err()
{
return HandlerOutcome::retry();
}
HandlerOutcome::ack()
}Source§impl<M, W, E, Pipe, Body> Slot<M, W, E, Pipe, Body>
impl<M, W, E, Pipe, Body> Slot<M, W, E, Pipe, Body>
Sourcepub async fn transaction(
&self,
) -> Result<TypedTransaction<<W as OwnedTransactions>::Transaction, &E, Slot<M, W, E, Pipe, Body>>, <W as Publisher>::Error>
pub async fn transaction( &self, ) -> Result<TypedTransaction<<W as OwnedTransactions>::Transaction, &E, Slot<M, W, E, Pipe, Body>>, <W as Publisher>::Error>
Opens an independent, caller-owned broker transaction and returns the
TypedTransaction that owns it: publishes buffer into the value, and
commit or abort consume it.
Every call opens its own transaction, so any number can be open on one entry at a time; the transaction’s typed entry admits what the slot’s own does. The buffer settles outside the slot, so its publishes land in the broker’s publish log and are not attributed to the slot (the documented capture boundary).
§Errors
Returns the publisher’s error when the broker refuses to open a transaction; pure client-buffer implementations are infallible in practice.
§Examples
use ruststream::runtime::{HandlerOutcome, Out};
use ruststream::{Outgoing, OutSlot, OwnedTransactions, subscriber};
use serde::Serialize;
#[derive(Outgoing, Serialize)]
#[outgoing(name = "ledger.settled")]
struct Settled {
id: u64,
}
#[derive(OutSlot)]
#[publishes(Settled)]
struct Ledger;
#[subscriber("ledger.orders")]
async fn settle(
order: &Order,
Out(ledger): Out<impl OwnedTransactions, Ledger, Settled>,
) -> HandlerOutcome {
let Ok(mut txn) = ledger.transaction().await else {
return HandlerOutcome::retry();
};
if txn.message(&Settled { id: order.id }).publish().await.is_err()
|| txn.commit().await.is_err()
{
return HandlerOutcome::retry();
}
HandlerOutcome::ack()
}Trait Implementations§
Source§impl<M, W, E, Pipe, Body> OutEntry<M, Body> for Slot<M, W, E, Pipe, Body>
impl<M, W, E, Pipe, Body> OutEntry<M, Body> for Slot<M, W, E, Pipe, Body>
Source§impl<M, W, E, Pipe, Body> OwnedTransactions for Slot<M, W, E, Pipe, Body>
impl<M, W, E, Pipe, Body> OwnedTransactions for Slot<M, W, E, Pipe, Body>
Source§type Transaction = <W as OwnedTransactions>::Transaction
type Transaction = <W as OwnedTransactions>::Transaction
transaction.Source§async fn transaction(
&self,
) -> Result<<Slot<M, W, E, Pipe, Body> as OwnedTransactions>::Transaction, <Slot<M, W, E, Pipe, Body> as Publisher>::Error>
async fn transaction( &self, ) -> Result<<Slot<M, W, E, Pipe, Body> as OwnedTransactions>::Transaction, <Slot<M, W, E, Pipe, Body> as Publisher>::Error>
Source§impl<M, W, E, Pipe, Body> Publisher for Slot<M, W, E, Pipe, Body>
impl<M, W, E, Pipe, Body> Publisher for Slot<M, W, E, Pipe, Body>
Source§type Error = <Pipe as OutPipeline<W>>::Error<<W as Publisher>::Error>
type Error = <Pipe as OutPipeline<W>>::Error<<W as Publisher>::Error>
publish.Source§type Options = <W as Publisher>::Options
type Options = <W as Publisher>::Options
QoS, a priority, an ordering key, whatever this
transport lets one message differ from the next in. Read moreSource§async fn publish(
&self,
msg: OutgoingMessage<'_, <<Slot<M, W, E, Pipe, Body> as Publisher>::Payload as PayloadForm>::Form<'_>>,
options: Option<&<Slot<M, W, E, Pipe, Body> as Publisher>::Options>,
) -> Result<(), <Slot<M, W, E, Pipe, Body> as Publisher>::Error>
async fn publish( &self, msg: OutgoingMessage<'_, <<Slot<M, W, E, Pipe, Body> as Publisher>::Payload as PayloadForm>::Form<'_>>, options: Option<&<Slot<M, W, E, Pipe, Body> as Publisher>::Options>, ) -> Result<(), <Slot<M, W, E, Pipe, Body> as Publisher>::Error>
Source§impl<M, W, E, Pipe, Body> RequestReply for Slot<M, W, E, Pipe, Body>
impl<M, W, E, Pipe, Body> RequestReply for Slot<M, W, E, Pipe, Body>
Source§type Reply = <W as RequestReply>::Reply
type Reply = <W as RequestReply>::Reply
Source§impl<M, W, E, Pipe, Body> TransactionalPublisher for Slot<M, W, E, Pipe, Body>
impl<M, W, E, Pipe, Body> TransactionalPublisher for Slot<M, W, E, Pipe, Body>
Source§async fn begin_transaction(
&self,
) -> Result<(), <Slot<M, W, E, Pipe, Body> as Publisher>::Error>
async fn begin_transaction( &self, ) -> Result<(), <Slot<M, W, E, Pipe, Body> as Publisher>::Error>
Auto Trait Implementations§
impl<M, W, E, Pipe, Body> Freeze for Slot<M, W, E, Pipe, Body>
impl<M, W, E, Pipe, Body> RefUnwindSafe for Slot<M, W, E, Pipe, Body>where
SlotPublisher<W, M>: RefUnwindSafe,
E: RefUnwindSafe,
Pipe: RefUnwindSafe,
PhantomData<fn() -> Body>: RefUnwindSafe,
impl<M, W, E, Pipe, Body> Send for Slot<M, W, E, Pipe, Body>
impl<M, W, E, Pipe, Body> Sync for Slot<M, W, E, Pipe, Body>
impl<M, W, E, Pipe, Body> Unpin for Slot<M, W, E, Pipe, Body>
impl<M, W, E, Pipe, Body> UnsafeUnpin for Slot<M, W, E, Pipe, Body>where
SlotPublisher<W, M>: UnsafeUnpin,
E: UnsafeUnpin,
Pipe: UnsafeUnpin,
PhantomData<fn() -> Body>: UnsafeUnpin,
impl<M, W, E, Pipe, Body> UnwindSafe for Slot<M, W, E, Pipe, Body>where
SlotPublisher<W, M>: UnwindSafe,
E: UnwindSafe,
Pipe: UnwindSafe,
PhantomData<fn() -> Body>: UnwindSafe,
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T, A> DynAccess<T> for A
impl<T, A> DynAccess<T> for A
Source§fn load(&self) -> DynGuard<T>
fn load(&self) -> DynGuard<T>
Access::load.Source§impl<E> Executor for E
impl<E> Executor for E
Source§type Task<T: Send + 'static> = <<E as Deref>::Target as Executor>::Task<T>
type Task<T: Send + 'static> = <<E as Deref>::Target as Executor>::Task<T>
Source§fn block_on<T, F>(&self, f: F) -> Twhere
F: Future<Output = T>,
fn block_on<T, F>(&self, f: F) -> Twhere
F: Future<Output = T>,
impl<T> Formattable for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreimpl<T> Parsable for T
Source§impl<P> PublishExt for P
impl<P> PublishExt for P
Source§fn message<'a, T>(
&'a self,
value: &'a T,
) -> PublishBuilder<&'a Self, MessageBody<'a, T>, UnnamedCodec, HeadersUnset, <T as OutgoingDestination>::Form>where
T: OutgoingDestination,
fn message<'a, T>(
&'a self,
value: &'a T,
) -> PublishBuilder<&'a Self, MessageBody<'a, T>, UnnamedCodec, HeadersUnset, <T as OutgoingDestination>::Form>where
T: OutgoingDestination,
#[derive(Outgoing)] value, encoded with the crate’s
default codec (name another one with with_codec(..)). A
Serialized value’s bytes leave as they are instead - the wire is
the type’s own (MessageWire), and no codec runs on it. Read moreSource§fn begin(
&self,
) -> impl Future<Output = Result<TransactionScope<'_, Self, UnnamedCodec, AnyDeclared>, Self::Error>> + Sendwhere
Self: Sized + TransactionalPublisher,
fn begin(
&self,
) -> impl Future<Output = Result<TransactionScope<'_, Self, UnnamedCodec, AnyDeclared>, Self::Error>> + Sendwhere
Self: Sized + TransactionalPublisher,
TransactionScope that
owns it. Read moreSource§fn owned_transaction(
&self,
) -> impl Future<Output = Result<TypedTransaction<Self::Transaction, UnnamedCodec, AnyDeclared>, Self::Error>> + Sendwhere
Self: Sized + OwnedTransactions,
fn owned_transaction(
&self,
) -> impl Future<Output = Result<TypedTransaction<Self::Transaction, UnnamedCodec, AnyDeclared>, Self::Error>> + Sendwhere
Self: Sized + OwnedTransactions,
TypedTransaction that owns it. Read moreimpl<T> PublishedThrough<DefaultSlot> for T
Source§impl<R> Reactor for R
impl<R> Reactor for R
Source§type TcpStream = <<R as Deref>::Target as Reactor>::TcpStream
type TcpStream = <<R as Deref>::Target as Reactor>::TcpStream
Source§type Sleep = <<R as Deref>::Target as Reactor>::Sleep
type Sleep = <<R as Deref>::Target as Reactor>::Sleep
Reactor::sleep)