1use std::fs;
5use std::sync::Arc;
6use std::sync::atomic::{AtomicBool, Ordering};
7
8use ruststream::{
9 Broker, ConnectedBroker, DefaultPublish, DescribeServer, OutgoingMessage, PairError,
10 PublishPolicy, Publisher, ServerSpec, Subscribe,
11};
12use sea_streamer_file::{
13 AutoStreamReset, FileConnectOptions, FileConsumerOptions, FileErr, FileId, FileProducer,
14 FileProducerOptions, FileStreamer,
15};
16use sea_streamer_types::{
17 ConsumerMode, ConsumerOptions as _, Producer as _, StreamErr, StreamKey, Streamer as _,
18};
19use tokio::sync::OnceCell;
20
21use crate::error::{SeaFileError, box_err};
22use crate::stream::FileStream;
23use crate::subscriber::FileSubscriber;
24use crate::wire;
25
26pub(crate) struct Core {
27 pub(crate) streamer: FileStreamer,
28 pub(crate) producer: FileProducer,
29 pub(crate) path: String,
30 pub(crate) closed: AtomicBool,
31}
32
33impl Core {
34 pub(crate) fn ensure_open(&self) -> Result<(), SeaFileError> {
35 if self.closed.load(Ordering::Acquire) {
36 return Err(SeaFileError::NotConnected);
37 }
38 Ok(())
39 }
40}
41
42impl std::fmt::Debug for Core {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 f.debug_struct("Core")
45 .field("path", &self.path)
46 .field("closed", &self.closed.load(Ordering::Relaxed))
47 .finish_non_exhaustive()
48 }
49}
50
51pub(crate) type CoreCell = Arc<OnceCell<Arc<Core>>>;
52
53const PRIME_STREAM: &str = "ruststream-internal";
56
57#[derive(Debug, Clone)]
72#[must_use]
73pub struct FileBroker {
74 path: String,
75 create: bool,
76 end_with_eos: bool,
77 beacon_interval: Option<u32>,
78 cell: CoreCell,
79}
80
81impl FileBroker {
82 pub fn new(path: impl Into<String>) -> Self {
84 Self {
85 path: path.into(),
86 create: true,
87 end_with_eos: false,
88 beacon_interval: None,
89 cell: Arc::new(OnceCell::new()),
90 }
91 }
92
93 pub fn existing_only(mut self) -> Self {
95 self.create = false;
96 self
97 }
98
99 pub fn end_with_eos(mut self) -> Self {
102 self.end_with_eos = true;
103 self
104 }
105
106 pub fn beacon_interval(mut self, bytes: u32) -> Self {
109 self.beacon_interval = Some(bytes);
110 self
111 }
112
113 #[must_use]
115 pub fn publisher(&self) -> FilePublisher {
116 FilePublisher {
117 cell: Arc::clone(&self.cell),
118 }
119 }
120}
121
122impl Broker for FileBroker {
123 type Error = SeaFileError;
124 type Connected = ConnectedFileBroker;
125
126 async fn connect(self) -> Result<Self::Connected, Self::Error> {
127 let core = self
128 .cell
129 .get_or_try_init(async || {
130 let mut options = FileConnectOptions::default();
131 if self.create {
132 options.set_create_if_not_exists(true);
133 }
134 if self.end_with_eos {
135 options.set_end_with_eos(true);
136 }
137 if let Some(interval) = self.beacon_interval {
138 options.set_beacon_interval(interval).map_err(|e| {
139 SeaFileError::Invalid(format!("invalid beacon interval: {e}"))
140 })?;
141 }
142 let file_id = FileId::new(self.path.clone());
143 let uri = file_id
144 .to_streamer_uri()
145 .map_err(|e| SeaFileError::Connect {
146 target: self.path.clone(),
147 source: box_err(e),
148 })?;
149 let streamer = FileStreamer::connect(uri, options).await.map_err(|e| {
150 SeaFileError::Connect {
151 target: self.path.clone(),
152 source: box_err(e),
153 }
154 })?;
155 let connect_err = |e: StreamErr<FileErr>| SeaFileError::Connect {
156 target: self.path.clone(),
157 source: box_err(e),
158 };
159 let producer = streamer
160 .create_generic_producer(FileProducerOptions::default())
161 .await
162 .map_err(connect_err)?;
163 let fresh = fs::metadata(&self.path).map_or(true, |meta| meta.len() <= 128);
167 if fresh {
168 let prime_key = StreamKey::new(PRIME_STREAM)
169 .map_err(|e| SeaFileError::Invalid(e.to_string()))?;
170 producer
171 .send_to(&prime_key, b"1".as_slice())
172 .map_err(connect_err)?
173 .await
174 .map_err(connect_err)?;
175 let mut flusher = producer.clone();
176 flusher.flush().await.map_err(connect_err)?;
177 }
178 Ok::<_, SeaFileError>(Arc::new(Core {
179 streamer,
180 producer,
181 path: self.path.clone(),
182 closed: AtomicBool::new(false),
183 }))
184 })
185 .await?
186 .clone();
187 Ok(ConnectedFileBroker {
188 core,
189 cell: self.cell,
190 })
191 }
192}
193
194impl DescribeServer for FileBroker {
195 fn describe_server(&self) -> ServerSpec {
196 ServerSpec::in_process("file").with_description(self.path.clone())
197 }
198}
199
200#[derive(Debug)]
202pub struct ConnectedFileBroker {
203 pub(crate) core: Arc<Core>,
204 cell: CoreCell,
206}
207
208impl ConnectedFileBroker {
209 #[must_use]
211 pub fn publisher(&self) -> FilePublisher {
212 FilePublisher {
213 cell: Arc::clone(&self.cell),
214 }
215 }
216
217 pub async fn subscribe_stream(
224 &self,
225 descriptor: FileStream,
226 ) -> Result<FileSubscriber, SeaFileError> {
227 descriptor.validate()?;
228 self.core.ensure_open()?;
229
230 let key = StreamKey::new(descriptor.stream())
231 .map_err(|e| SeaFileError::Invalid(format!("'{}': {e}", descriptor.stream())))?;
232 let mut options = FileConsumerOptions::new(ConsumerMode::RealTime);
233 options.set_auto_stream_reset(if descriptor.replay_value() {
237 AutoStreamReset::Earliest
238 } else {
239 AutoStreamReset::Latest
240 });
241 options.set_live_streaming(!descriptor.replay_value());
242 let consumer = self
243 .core
244 .streamer
245 .create_consumer(&[key], options)
246 .await
247 .map_err(|e| SeaFileError::Subscribe {
248 stream: descriptor.stream().to_owned(),
249 source: box_err(e),
250 })?;
251 Ok(FileSubscriber::spawn(
252 descriptor.stream().to_owned(),
253 consumer,
254 descriptor.replay_value(),
255 ))
256 }
257}
258
259impl ConnectedBroker for ConnectedFileBroker {
260 type Error = SeaFileError;
261 type Closed = ();
262
263 async fn shutdown(self) -> Result<(), Self::Error> {
264 self.core.closed.store(true, Ordering::Release);
265 match self.core.streamer.clone().disconnect().await {
269 Ok(()) | Err(StreamErr::Backend(FileErr::ProducerEnded)) => Ok(()),
270 Err(e) => Err(SeaFileError::Connect {
271 target: self.core.path.clone(),
272 source: box_err(e),
273 }),
274 }
275 }
276}
277
278impl Subscribe for ConnectedFileBroker {
279 type Subscriber = FileSubscriber;
280
281 async fn subscribe(&self, name: &str) -> Result<Self::Subscriber, Self::Error> {
282 self.subscribe_stream(FileStream::new(name)).await
283 }
284}
285
286#[derive(Clone)]
291pub struct FilePublisher {
292 cell: CoreCell,
293}
294
295impl std::fmt::Debug for FilePublisher {
296 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
297 f.debug_struct("FilePublisher").finish_non_exhaustive()
298 }
299}
300
301impl Publisher for FilePublisher {
302 type Error = SeaFileError;
303
304 async fn publish(&self, msg: OutgoingMessage<'_>) -> Result<(), Self::Error> {
305 let core = self.cell.get().ok_or(SeaFileError::NotConnected)?;
306 core.ensure_open()?;
307 let producer = &core.producer;
308 let key = StreamKey::new(msg.name())
309 .map_err(|e| SeaFileError::Invalid(format!("'{}': {e}", msg.name())))?;
310 let payload = wire::encode(msg.headers(), msg.payload(), false);
311 producer
312 .send_to(&key, payload.as_slice())
313 .map_err(|e| SeaFileError::Publish {
314 stream: msg.name().to_owned(),
315 source: box_err(e),
316 })?
317 .await
318 .map_err(|e| SeaFileError::Publish {
319 stream: msg.name().to_owned(),
320 source: box_err(e),
321 })?;
322 let mut flusher = producer.clone();
325 flusher.flush().await.map_err(|e| SeaFileError::Publish {
326 stream: msg.name().to_owned(),
327 source: box_err(e),
328 })
329 }
330}
331
332#[derive(Debug, Clone, Copy, Default)]
343#[must_use]
344pub struct FilePublish;
345
346impl PublishPolicy<ConnectedFileBroker> for FilePublish {
347 type Live = FilePublisher;
348
349 async fn pair(self, connected: &ConnectedFileBroker) -> Result<Self::Live, PairError> {
350 Ok(connected.publisher())
351 }
352}
353
354impl DefaultPublish for ConnectedFileBroker {
355 type Policy = FilePublish;
356}