1use std::sync::Arc;
5use std::sync::atomic::{AtomicBool, Ordering};
6
7use futures::Stream;
8use ruststream::{
9 Broker, ConnectedBroker, DefaultPublish, DescribeServer, OutgoingMessage, PairError,
10 PublishPolicy, Publisher, ServerSpec, Subscribe, Subscriber,
11};
12use sea_streamer_stdio::{StdioConnectOptions, StdioProducer, StdioProducerOptions, StdioStreamer};
13use sea_streamer_types::{
14 Consumer as _, ConsumerMode, ConsumerOptions as _, Producer as _, StreamKey, Streamer as _,
15 StreamerUri,
16};
17use tokio::sync::{OnceCell, mpsc};
18
19use crate::error::{SeaFileError, box_err};
20use crate::message::SeaMessage;
21use crate::wire;
22
23pub(crate) struct StdioCore {
24 pub(crate) streamer: StdioStreamer,
25 pub(crate) closed: AtomicBool,
26}
27
28impl StdioCore {
29 fn ensure_open(&self) -> Result<(), SeaFileError> {
30 if self.closed.load(Ordering::Acquire) {
31 return Err(SeaFileError::NotConnected);
32 }
33 Ok(())
34 }
35}
36
37impl std::fmt::Debug for StdioCore {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 f.debug_struct("StdioCore")
40 .field("closed", &self.closed.load(Ordering::Relaxed))
41 .finish_non_exhaustive()
42 }
43}
44
45type StdioCell = Arc<OnceCell<Arc<StdioCore>>>;
46
47#[derive(Debug, Clone, Default)]
59#[must_use]
60pub struct StdioBroker {
61 loopback: bool,
62 cell: StdioCell,
63}
64
65impl StdioBroker {
66 pub fn new() -> Self {
68 Self::default()
69 }
70
71 pub fn loopback(mut self) -> Self {
73 self.loopback = true;
74 self
75 }
76
77 #[must_use]
79 pub fn publisher(&self) -> StdioPublisher {
80 StdioPublisher {
81 cell: Arc::clone(&self.cell),
82 producer: Arc::new(OnceCell::new()),
83 }
84 }
85}
86
87impl Broker for StdioBroker {
88 type Error = SeaFileError;
89 type Connected = ConnectedStdioBroker;
90
91 async fn connect(self) -> Result<Self::Connected, Self::Error> {
92 let core = self
93 .cell
94 .get_or_try_init(async || {
95 let mut options = StdioConnectOptions::default();
96 options.set_loopback(self.loopback);
97 let streamer = StdioStreamer::connect(StreamerUri::zero(), options)
98 .await
99 .map_err(|e| SeaFileError::Connect {
100 target: "stdio".to_owned(),
101 source: box_err(e),
102 })?;
103 Ok::<_, SeaFileError>(Arc::new(StdioCore {
104 streamer,
105 closed: AtomicBool::new(false),
106 }))
107 })
108 .await?
109 .clone();
110 Ok(ConnectedStdioBroker {
111 core,
112 cell: self.cell,
113 })
114 }
115}
116
117impl DescribeServer for StdioBroker {
118 fn describe_server(&self) -> ServerSpec {
119 ServerSpec::in_process("stdio")
120 }
121}
122
123#[derive(Debug)]
125pub struct ConnectedStdioBroker {
126 core: Arc<StdioCore>,
127 cell: StdioCell,
129}
130
131impl ConnectedStdioBroker {
132 #[must_use]
134 pub fn publisher(&self) -> StdioPublisher {
135 StdioPublisher {
136 cell: Arc::clone(&self.cell),
137 producer: Arc::new(OnceCell::new()),
138 }
139 }
140}
141
142impl ConnectedBroker for ConnectedStdioBroker {
143 type Error = SeaFileError;
144 type Closed = ();
145
146 async fn shutdown(self) -> Result<(), Self::Error> {
147 self.core.closed.store(true, Ordering::Release);
148 self.core
152 .streamer
153 .clone()
154 .disconnect()
155 .await
156 .map_err(|e| SeaFileError::Connect {
157 target: "stdio".to_owned(),
158 source: box_err(e),
159 })
160 }
161}
162
163impl Subscribe for ConnectedStdioBroker {
164 type Subscriber = StdioSubscriber;
165
166 async fn subscribe(&self, name: &str) -> Result<Self::Subscriber, Self::Error> {
167 self.core.ensure_open()?;
168 let key =
169 StreamKey::new(name).map_err(|e| SeaFileError::Invalid(format!("'{name}': {e}")))?;
170 let consumer = self
171 .core
172 .streamer
173 .create_consumer(
174 &[key],
175 sea_streamer_stdio::StdioConsumerOptions::new(ConsumerMode::RealTime),
176 )
177 .await
178 .map_err(|e| SeaFileError::Subscribe {
179 stream: name.to_owned(),
180 source: box_err(e),
181 })?;
182
183 let (tx, rx) = mpsc::channel(64);
184 let stream_name = name.to_owned();
185 tokio::spawn(async move {
186 loop {
187 tokio::select! {
188 () = tx.closed() => break,
189 next = consumer.next() => match next {
190 Ok(message) => {
191 if tx.send(Ok(SeaMessage::new(&message))).await.is_err() {
192 break;
193 }
194 }
195 Err(err) => {
196 let _ = tx
197 .send(Err(SeaFileError::Receive {
198 stream: stream_name.clone(),
199 source: box_err(err),
200 }))
201 .await;
202 break;
203 }
204 },
205 }
206 }
207 });
208 Ok(StdioSubscriber {
209 stream: name.to_owned(),
210 rx,
211 })
212 }
213}
214
215impl DefaultPublish for ConnectedStdioBroker {
216 type Policy = StdioPublish;
217}
218
219pub struct StdioSubscriber {
224 stream: String,
225 rx: mpsc::Receiver<Result<SeaMessage, SeaFileError>>,
226}
227
228impl std::fmt::Debug for StdioSubscriber {
229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230 f.debug_struct("StdioSubscriber")
231 .field("stream", &self.stream)
232 .finish_non_exhaustive()
233 }
234}
235
236impl Subscriber for StdioSubscriber {
237 type Message = SeaMessage;
238 type Error = SeaFileError;
239
240 fn stream(&mut self) -> impl Stream<Item = Result<SeaMessage, SeaFileError>> + Send + '_ {
241 futures::stream::poll_fn(move |cx| self.rx.poll_recv(cx))
245 }
246}
247
248#[derive(Clone)]
254pub struct StdioPublisher {
255 cell: StdioCell,
256 producer: Arc<OnceCell<StdioProducer>>,
257}
258
259impl std::fmt::Debug for StdioPublisher {
260 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261 f.debug_struct("StdioPublisher").finish_non_exhaustive()
262 }
263}
264
265impl Publisher for StdioPublisher {
266 type Error = SeaFileError;
267
268 async fn publish(&self, msg: OutgoingMessage<'_>) -> Result<(), Self::Error> {
269 let core = self.cell.get().ok_or(SeaFileError::NotConnected)?;
270 core.ensure_open()?;
271 if msg.payload().is_empty() && msg.headers().is_empty() {
272 return Err(SeaFileError::Invalid(
273 "stdio drops empty lines; an empty message cannot be transmitted".into(),
274 ));
275 }
276 let producer = self
277 .producer
278 .get_or_try_init(async || {
279 core.streamer
280 .create_generic_producer(StdioProducerOptions::default())
281 .await
282 .map_err(|e| SeaFileError::Publish {
283 stream: msg.name().to_owned(),
284 source: box_err(e),
285 })
286 })
287 .await?;
288 let key = StreamKey::new(msg.name())
289 .map_err(|e| SeaFileError::Invalid(format!("'{}': {e}", msg.name())))?;
290 let payload = wire::encode(msg.headers(), msg.payload(), true);
292 producer
293 .send_to(&key, payload.as_slice())
294 .map_err(|e| SeaFileError::Publish {
295 stream: msg.name().to_owned(),
296 source: box_err(e),
297 })?
298 .await
299 .map(|_| ())
300 .map_err(|e| SeaFileError::Publish {
301 stream: msg.name().to_owned(),
302 source: box_err(e),
303 })
304 }
305}
306
307#[derive(Debug, Clone, Copy, Default)]
318#[must_use]
319pub struct StdioPublish;
320
321impl PublishPolicy<ConnectedStdioBroker> for StdioPublish {
322 type Live = StdioPublisher;
323
324 async fn pair(self, connected: &ConnectedStdioBroker) -> Result<Self::Live, PairError> {
325 Ok(connected.publisher())
326 }
327}