Skip to main content

Slot

Struct Slot 

Source
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>
where M: OutSlot, W: Publisher, E: Codec + Send + Sync, Pipe: OutPipeline<W>,

Source

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>
where Body: ContainsMessage<T, Index>, T: OutgoingDestination + PublishedThrough<M>,

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>
where M: OutSlot, W: TransactionalPublisher, E: Send + Sync, Pipe: Send + Sync,

Source

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>
where M: OutSlot, W: OwnedTransactions, E: Send + Sync, Pipe: Send + Sync,

Source

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> Debug for Slot<M, W, E, Pipe, Body>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl<M, W, E, Pipe, Body> Deref for Slot<M, W, E, Pipe, Body>

Source§

type Target = W

The resulting type after dereferencing.
Source§

fn deref(&self) -> &W

Dereferences the value.
Source§

impl<M, W, E, Pipe, Body> OutEntry<M, Body> for Slot<M, W, E, Pipe, Body>
where W: Send + Sync, E: Codec, Pipe: OutPipeline<W>,

Source§

type Wire = W

The wired live value: the bound policy’s Live form, which a body bounds with the broker capability it needs.
Source§

type Enc = E

The codec the entry’s typed publishes encode with: the include site’s own, or the one a .codec(..) named for this slot.
Source§

type Pipe = Pipe

The publish path every message leaving the entry travels: the app’s publish pipeline with the slot’s .transform(..) steps composed on top. It is stated over the wired value, because a transform there writes that publisher’s own per-message options.
Source§

impl<M, W, E, Pipe, Body> OwnedTransactions for Slot<M, W, E, Pipe, Body>
where M: OutSlot, W: OwnedTransactions, E: Send + Sync, Pipe: OutPipeline<W>,

Source§

type Transaction = <W as OwnedTransactions>::Transaction

The buffer-owning transaction opened by 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>

Opens a new transaction owned by the returned value. Read more
Source§

impl<M, W, E, Pipe, Body> Publisher for Slot<M, W, E, Pipe, Body>
where M: OutSlot, W: Publisher, E: Send + Sync, Pipe: OutPipeline<W>,

Source§

type Payload = <W as Publisher>::Payload

How this transport consumes the payload: Lend where it reads the bytes and keeps nothing, Take where its client keeps them. Read more
Source§

type Error = <Pipe as OutPipeline<W>>::Error<<W as Publisher>::Error>

The error type returned by publish.
Source§

type Options = <W as Publisher>::Options

The broker’s per-message settings: a QoS, a priority, an ordering key, whatever this transport lets one message differ from the next in. Read more
Source§

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>

Publishes a message to the broker, with the per-message settings the call site adjusted. Read more
Source§

fn base_headers(&self) -> Option<&HeaderMap>

The headers this publisher contributes to every message it sends, underneath whatever the message itself names. Read more
Source§

impl<M, W, E, Pipe, Body> RequestReply for Slot<M, W, E, Pipe, Body>
where M: OutSlot, W: RequestReply, E: Send + Sync, Pipe: OutPipeline<W>,

Source§

type Reply = <W as RequestReply>::Reply

The reply message type.
Source§

async fn request( &self, msg: OutgoingMessage<'_, <<Slot<M, W, E, Pipe, Body> as Publisher>::Payload as PayloadForm>::Form<'_>>, timeout: Duration, ) -> Result<<Slot<M, W, E, Pipe, Body> as RequestReply>::Reply, <Slot<M, W, E, Pipe, Body> as Publisher>::Error>

Publishes msg and awaits a single correlated reply, or fails after timeout. Read more
Source§

impl<M, W, E, Pipe, Body> TransactionalPublisher for Slot<M, W, E, Pipe, Body>
where M: OutSlot, W: TransactionalPublisher, E: Send + Sync, Pipe: OutPipeline<W>,

Source§

async fn begin_transaction( &self, ) -> Result<(), <Slot<M, W, E, Pipe, Body> as Publisher>::Error>

Begins a new transaction on this publisher. Read more
Source§

async fn commit( &self, ) -> Result<(), <Slot<M, W, E, Pipe, Body> as Publisher>::Error>

Commits the active transaction, making all buffered messages visible atomically. Read more
Source§

async fn abort( &self, ) -> Result<(), <Slot<M, W, E, Pipe, Body> as Publisher>::Error>

Aborts the active transaction, discarding all buffered messages. Read more

Auto Trait Implementations§

§

impl<M, W, E, Pipe, Body> Freeze for Slot<M, W, E, Pipe, Body>
where SlotPublisher<W, M>: Freeze, E: Freeze, Pipe: Freeze, PhantomData<fn() -> Body>: Freeze,

§

impl<M, W, E, Pipe, Body> RefUnwindSafe for Slot<M, W, E, Pipe, Body>

§

impl<M, W, E, Pipe, Body> Send for Slot<M, W, E, Pipe, Body>
where SlotPublisher<W, M>: Send, E: Send, Pipe: Send, PhantomData<fn() -> Body>: Send,

§

impl<M, W, E, Pipe, Body> Sync for Slot<M, W, E, Pipe, Body>
where SlotPublisher<W, M>: Sync, E: Sync, Pipe: Sync, PhantomData<fn() -> Body>: Sync,

§

impl<M, W, E, Pipe, Body> Unpin for Slot<M, W, E, Pipe, Body>
where SlotPublisher<W, M>: Unpin, E: Unpin, Pipe: Unpin, PhantomData<fn() -> Body>: Unpin,

§

impl<M, W, E, Pipe, Body> UnsafeUnpin for Slot<M, W, E, Pipe, Body>

§

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<T, A, P> Access<T> for P
where A: Access<T> + ?Sized, P: Deref<Target = A>,

Source§

type Guard = <A as Access<T>>::Guard

A guard object containing the value and keeping it alive. Read more
Source§

fn load(&self) -> <P as Access<T>>::Guard

The loading method. Read more
Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CompatExt for T

Source§

fn compat(self) -> Compat<T> ⓘ
where T: Sized,

Applies the Compat adapter by value. Read more
Source§

fn compat_ref(&self) -> Compat<&T> ⓘ

Applies the Compat adapter by shared reference. Read more
Source§

fn compat_mut(&mut self) -> Compat<&mut T> ⓘ

Applies the Compat adapter by mutable reference. Read more
Source§

impl<T, A> DynAccess<T> for A
where A: Access<T>, <A as Access<T>>::Guard: 'static,

Source§

fn load(&self) -> DynGuard<T>

The equivalent of Access::load.
Source§

impl<E> Executor for E
where E: Deref, <E as Deref>::Target: Sized + Executor,

Source§

type Task<T: Send + 'static> = <<E as Deref>::Target as Executor>::Task<T>

The type representing the tasks the are returned when spawning a Future on the executor
Source§

fn block_on<T, F>(&self, f: F) -> T
where F: Future<Output = T>,

Block on a future until completion
Source§

fn spawn<T, F>(&self, f: F) -> Task<<E as Executor>::Task<T>> ⓘ
where T: Send + 'static, F: Future<Output = T> + Send + 'static,

Spawn a future and return a handle to track its completion.
Source§

fn spawn_blocking<T, F>(&self, f: F) -> Task<<E as Executor>::Task<T>> ⓘ
where T: Send + 'static, F: FnOnce() -> T + Send + 'static,

Convert a blocking task into a future, spawning it on a dedicated thread pool
Source§

impl<T> Formattable for T
where T: Deref, <T as Deref>::Target: Formattable,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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 more
Source§

impl<T> Parsable for T
where T: Deref, <T as Deref>::Target: Parsable,

Source§

impl<P> PublishExt for P
where P: Publisher + ?Sized,

Source§

fn message<'a, T>( &'a self, value: &'a T, ) -> PublishBuilder<&'a Self, MessageBody<'a, T>, UnnamedCodec, HeadersUnset, <T as OutgoingDestination>::Form>

Starts a typed publish of a #[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 more
Source§

fn begin( &self, ) -> impl Future<Output = Result<TransactionScope<'_, Self, UnnamedCodec, AnyDeclared>, Self::Error>> + Send

Opens a broker transaction on this publisher and returns the TransactionScope that owns it. Read more
Source§

fn owned_transaction( &self, ) -> impl Future<Output = Result<TypedTransaction<Self::Transaction, UnnamedCodec, AnyDeclared>, Self::Error>> + Send
where Self: Sized + OwnedTransactions,

Opens an owned broker transaction and returns the TypedTransaction that owns it. Read more
Source§

impl<T> PublishedThrough<DefaultSlot> for T

Source§

impl<R> Reactor for R
where R: Deref, <R as Deref>::Target: Sized + Reactor,

Source§

type TcpStream = <<R as Deref>::Target as Reactor>::TcpStream

The type representing a TCP stream (after tcp_connect) for this reactor
Source§

type Sleep = <<R as Deref>::Target as Reactor>::Sleep

A future that completes after a requested duration has elapsed (see Reactor::sleep)
Source§

fn register<H>( &self, socket: H, ) -> Result<impl AsyncRead + AsyncWrite + Send + Unpin + 'static, Error>
where H: Read + Write + AsSysFd + Send + 'static,

Register a synchronous handle, returning an asynchronous one
Source§

fn sleep(&self, dur: Duration) -> <R as Reactor>::Sleep

Sleep for the given duration
Source§

fn interval( &self, dur: Duration, ) -> impl Stream<Item = Instant> + Send + 'static

Stream that yields at every given interval
Source§

fn tcp_connect_addr( &self, addr: SocketAddr, ) -> impl Future<Output = Result<<R as Reactor>::TcpStream, Error>> + Send + 'static

Create a TcpStream by connecting to a specific pre-resolved address
Source§

fn tcp_connect<A>( &self, addrs: A, ) -> impl Future<Output = Result<Self::TcpStream, Error>> + Send
where A: AsyncToSocketAddrs + Send, Self: Sized + Sync,

Create a TcpStream by connecting to a remote host
Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more