Skip to main content

rustrade_data/subscriber/
shared_stream.rs

1//! The market stream of every provider whose streams share one connection.
2//!
3//! Such a provider hands each stream a view of the shared connection rather than a socket of its
4//! own — see [`Subscriber::Transport`] — which the standard WebSocket initialisation, built to
5//! split and drive a socket, cannot read.
6//! [`SharedStream`](crate::subscriber::shared_stream::SharedStream) reads that view instead, and
7//! parses and transforms exactly as the standard stream does.
8
9use crate::{
10    Identifier, MarketStream, SnapshotFetcher,
11    error::DataError,
12    exchange::Connector,
13    instrument::InstrumentData,
14    process_buffered_events,
15    subscriber::{Subscribed, Subscriber},
16    subscription::{Subscription, SubscriptionKind},
17    transformer::ExchangeTransformer,
18};
19use futures::Stream;
20use rustrade_integration::{
21    Transformer,
22    protocol::{
23        StreamParser,
24        websocket::{WebSocketSerdeParser, WsError, WsMessage},
25    },
26    stream::ExchangeStream,
27};
28use std::{
29    pin::Pin,
30    task::{Context, Poll},
31};
32use tokio::sync::mpsc;
33
34/// One stream's view of a connection it shares with other streams: the raw frames carrying its own
35/// subscriptions, in the order the socket delivered them, and whatever the connection handed it on
36/// attaching.
37///
38/// Implemented by each sharing provider's attachment, such as `AlpacaAttachment`. Sealed: only
39/// this crate's providers share a connection.
40pub trait SharedTransport:
41    Stream<Item = Result<WsMessage, WsError>> + Unpin + Send + sealed::Sealed
42{
43    /// What the connection hands a stream on attaching, for its transformer: `()` for a provider
44    /// with nothing to hand over.
45    type Attached: Send;
46
47    /// Take what the connection handed the stream on attaching. Called once, before any frame is
48    /// read.
49    fn take_attached(&mut self) -> Self::Attached;
50}
51
52pub(crate) mod sealed {
53    /// Seals [`SharedTransport`](super::SharedTransport).
54    pub trait Sealed {}
55}
56
57/// A transformer that takes what its stream's connection handed over on attaching.
58///
59/// Every transformer takes `()`, the nothing most providers hand over. A provider handing over
60/// more implements it for its own transformer, which receives it before any frame is processed.
61pub trait AttachedTransformer<Attached> {
62    /// Take what the connection handed the stream on attaching.
63    fn attached(&mut self, attached: Attached);
64}
65
66impl<T> AttachedTransformer<()> for T {
67    fn attached(&mut self, (): ()) {}
68}
69
70/// The market stream every provider sharing a connection among its streams is served over.
71///
72/// Parses and transforms exactly as the standard WebSocket stream does. It differs in what it
73/// reads — its [`SharedTransport`] view of the subscriber's shared connection — and in handing the
74/// transformer what the connection handed the stream on attaching.
75///
76/// Nothing is sent on the stream's behalf: the connection alone writes to the shared socket, so
77/// the transformer is handed a sender nobody reads.
78// The bounds are on the struct rather than only its impls because the inner `ExchangeStream`
79// carries them on its own definition; there is no way to name the field type without them.
80pub struct SharedStream<Transport, StreamTransformer>
81where
82    Transport: SharedTransport,
83    StreamTransformer: Transformer,
84    WebSocketSerdeParser: StreamParser<StreamTransformer::Input>,
85{
86    inner: ExchangeStream<WebSocketSerdeParser, Transport, StreamTransformer>,
87}
88
89// Written by hand: a derive would demand `Debug` of the transformer's output and error types
90// through the buffered events, which says nothing useful about the stream.
91impl<Transport, StreamTransformer> std::fmt::Debug for SharedStream<Transport, StreamTransformer>
92where
93    Transport: SharedTransport,
94    StreamTransformer: Transformer,
95    WebSocketSerdeParser: StreamParser<StreamTransformer::Input>,
96{
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        f.debug_struct("SharedStream")
99            .field("buffered", &self.inner.buffer.len())
100            .finish_non_exhaustive()
101    }
102}
103
104impl<Transport, StreamTransformer> Stream for SharedStream<Transport, StreamTransformer>
105where
106    Transport: SharedTransport,
107    StreamTransformer: Transformer,
108    WebSocketSerdeParser: StreamParser<StreamTransformer::Input>,
109    ExchangeStream<WebSocketSerdeParser, Transport, StreamTransformer>:
110        Stream<Item = Result<StreamTransformer::Output, StreamTransformer::Error>> + Unpin,
111{
112    type Item = Result<StreamTransformer::Output, StreamTransformer::Error>;
113
114    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
115        // The transport is `Unpin` and `MarketStream` requires `Unpin` regardless, so the inner
116        // stream can be re-pinned without a projection.
117        Pin::new(&mut self.get_mut().inner).poll_next(cx)
118    }
119}
120
121impl<Exchange, Instrument, Kind, Transport, StreamTransformer>
122    MarketStream<Exchange, Instrument, Kind> for SharedStream<Transport, StreamTransformer>
123where
124    Exchange: Connector + Send + Sync,
125    Exchange::Subscriber: Subscriber<Transport = Transport>,
126    Transport: SharedTransport,
127    Instrument: InstrumentData,
128    Kind: SubscriptionKind + Send + Sync,
129    Kind::Event: Send + Sync,
130    StreamTransformer: ExchangeTransformer<Exchange, Instrument::Key, Kind>
131        + AttachedTransformer<Transport::Attached>
132        + Send,
133    WebSocketSerdeParser:
134        StreamParser<StreamTransformer::Input, Message = WsMessage, Error = WsError>,
135{
136    /// Attach to the subscriber's shared connection, and assemble the stream around the kind's
137    /// transformer.
138    ///
139    /// What the connection handed the stream on attaching reaches the transformer **before** any
140    /// frame is processed. The connection routes a batch's frames into its attachment from the
141    /// moment the batch is registered, so frames can already be waiting there when the attach
142    /// returns; handing it over any later would let them past it.
143    async fn init<SnapFetcher>(
144        subscriber: &Exchange::Subscriber,
145        subscriptions: &[Subscription<Exchange, Instrument, Kind>],
146    ) -> Result<Self, DataError>
147    where
148        SnapFetcher: SnapshotFetcher<Exchange, Kind>,
149        Subscription<Exchange, Instrument, Kind>:
150            Identifier<Exchange::Channel> + Identifier<Exchange::Market>,
151    {
152        let Subscribed {
153            transport: mut attachment,
154            map: instrument_map,
155            buffered_websocket_events,
156        } = subscriber.subscribe(subscriptions).await?;
157
158        // Taken through the generic path, though no sharing provider fetches initial snapshots
159        // today, so a future kind that needs one is not silently ignored.
160        let initial_snapshots = SnapFetcher::fetch_snapshots(subscriptions).await?;
161
162        let (ws_sink_tx, _unread) = mpsc::unbounded_channel();
163
164        let mut transformer =
165            StreamTransformer::init(instrument_map, &initial_snapshots, ws_sink_tx).await?;
166        transformer.attached(attachment.take_attached());
167
168        // Empty for a sharing provider, whose connection routes frames into the attachment rather
169        // than reading ahead of it, but processed through the generic path so the `Subscribed`
170        // contract holds whatever the subscriber hands back.
171        let mut processed = process_buffered_events::<WebSocketSerdeParser, _>(
172            &mut transformer,
173            buffered_websocket_events,
174        );
175        processed.extend(initial_snapshots.into_iter().map(Ok));
176
177        Ok(Self {
178            inner: ExchangeStream::new(attachment, transformer, processed),
179        })
180    }
181}