1use crate::{Error, Result, coding};
16
17use super::{Group, GroupConsumer, GroupProducer};
18
19use std::{
20 collections::{HashSet, VecDeque},
21 task::{Poll, ready},
22 time::Duration,
23};
24
25const MAX_GROUP_AGE: Duration = Duration::from_secs(5);
28
29#[derive(Clone, Debug, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct Track {
33 pub name: String,
35 pub priority: u8,
37}
38
39impl Track {
40 pub fn new<T: Into<String>>(name: T) -> Self {
42 Self {
43 name: name.into(),
44 priority: 0,
45 }
46 }
47
48 pub fn with_priority(mut self, priority: u8) -> Self {
50 self.priority = priority;
51 self
52 }
53
54 pub fn produce(self) -> TrackProducer {
56 TrackProducer::new(self)
57 }
58}
59
60#[derive(Default)]
61struct State {
62 groups: VecDeque<Option<(GroupProducer, web_async::time::Instant)>>,
64 duplicates: HashSet<u64>,
65 offset: usize,
66 max_sequence: Option<u64>,
67 final_sequence: Option<u64>,
68 abort: Option<Error>,
69}
70
71impl State {
72 fn poll_recv_group(&self, index: usize, min_sequence: u64) -> Poll<Result<Option<(GroupConsumer, usize)>>> {
76 let start = index.saturating_sub(self.offset);
77 for (i, slot) in self.groups.iter().enumerate().skip(start) {
78 if let Some((group, _)) = slot
79 && group.sequence >= min_sequence
80 {
81 return Poll::Ready(Ok(Some((group.consume(), self.offset + i))));
82 }
83 }
84
85 if self.final_sequence.is_some() {
87 Poll::Ready(Ok(None))
88 } else if let Some(err) = &self.abort {
89 Poll::Ready(Err(err.clone()))
90 } else {
91 Poll::Pending
92 }
93 }
94
95 fn poll_read_frame(
99 &self,
100 index: usize,
101 next_sequence: u64,
102 waiter: &kio::Waiter,
103 ) -> Poll<Result<Option<(bytes::Bytes, usize, u64)>>> {
104 let start = index.saturating_sub(self.offset);
105 let mut pending_seen = false;
106 for (i, slot) in self.groups.iter().enumerate().skip(start) {
107 let Some((group, _)) = slot else { continue };
108 if group.sequence < next_sequence {
109 continue;
110 }
111
112 let mut consumer = group.consume();
113 match consumer.poll_read_frame(waiter) {
114 Poll::Ready(Ok(Some(frame))) => {
115 return Poll::Ready(Ok(Some((frame, self.offset + i, group.sequence))));
116 }
117 Poll::Ready(Ok(None)) => continue,
118 Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
119 Poll::Pending => {
120 pending_seen = true;
121 continue;
122 }
123 }
124 }
125
126 if pending_seen {
129 Poll::Pending
130 } else if self.final_sequence.is_some() {
131 Poll::Ready(Ok(None))
132 } else if let Some(err) = &self.abort {
133 Poll::Ready(Err(err.clone()))
134 } else {
135 Poll::Pending
136 }
137 }
138
139 fn poll_get_group(&self, sequence: u64) -> Poll<Result<Option<GroupConsumer>>> {
140 for (group, _) in self.groups.iter().flatten() {
142 if group.sequence == sequence {
143 return Poll::Ready(Ok(Some(group.consume())));
144 }
145 }
146
147 if let Some(fin) = self.final_sequence
149 && sequence >= fin
150 {
151 return Poll::Ready(Ok(None));
152 }
153
154 if let Some(err) = &self.abort {
155 return Poll::Ready(Err(err.clone()));
156 }
157
158 Poll::Pending
159 }
160
161 fn poll_closed(&self) -> Poll<Result<()>> {
162 if self.final_sequence.is_some() {
163 Poll::Ready(Ok(()))
164 } else if let Some(err) = &self.abort {
165 Poll::Ready(Err(err.clone()))
166 } else {
167 Poll::Pending
168 }
169 }
170
171 fn evict_expired(&mut self, now: web_async::time::Instant) {
178 for slot in self.groups.iter_mut() {
179 let Some((group, created_at)) = slot else { continue };
180
181 if Some(group.sequence) == self.max_sequence {
182 continue;
183 }
184
185 if now.duration_since(*created_at) <= MAX_GROUP_AGE {
186 break;
187 }
188
189 self.duplicates.remove(&group.sequence);
190 *slot = None;
191 }
192
193 while let Some(None) = self.groups.front() {
195 self.groups.pop_front();
196 self.offset += 1;
197 }
198 }
199
200 fn poll_finished(&self) -> Poll<Result<u64>> {
201 if let Some(fin) = self.final_sequence {
202 Poll::Ready(Ok(fin))
203 } else if let Some(err) = &self.abort {
204 Poll::Ready(Err(err.clone()))
205 } else {
206 Poll::Pending
207 }
208 }
209}
210
211pub struct TrackProducer {
213 info: Track,
214 state: kio::Producer<State>,
215}
216
217impl std::ops::Deref for TrackProducer {
218 type Target = Track;
219
220 fn deref(&self) -> &Self::Target {
221 &self.info
222 }
223}
224
225impl TrackProducer {
226 pub fn new(info: Track) -> Self {
228 Self {
229 info,
230 state: kio::Producer::default(),
231 }
232 }
233
234 pub fn create_group(&mut self, info: Group) -> Result<GroupProducer> {
236 let group = info.produce();
237
238 let mut state = self.modify()?;
239 if let Some(fin) = state.final_sequence
240 && group.sequence >= fin
241 {
242 return Err(Error::Closed);
243 }
244
245 if !state.duplicates.insert(group.sequence) {
246 return Err(Error::Duplicate);
247 }
248
249 let now = web_async::time::Instant::now();
250 state.max_sequence = Some(state.max_sequence.unwrap_or(0).max(group.sequence));
251 state.groups.push_back(Some((group.clone(), now)));
252 state.evict_expired(now);
253
254 Ok(group)
255 }
256
257 pub fn append_group(&mut self) -> Result<GroupProducer> {
259 let mut state = self.modify()?;
260 let sequence = match state.max_sequence {
261 Some(s) => s.checked_add(1).ok_or(coding::BoundsExceeded)?,
262 None => 0,
263 };
264 if let Some(fin) = state.final_sequence
265 && sequence >= fin
266 {
267 return Err(Error::Closed);
268 }
269
270 let group = Group { sequence }.produce();
271
272 let now = web_async::time::Instant::now();
273 state.duplicates.insert(sequence);
274 state.max_sequence = Some(sequence);
275 state.groups.push_back(Some((group.clone(), now)));
276 state.evict_expired(now);
277
278 Ok(group)
279 }
280
281 pub fn write_frame<B: Into<bytes::Bytes>>(&mut self, frame: B) -> Result<()> {
283 let mut group = self.append_group()?;
284 group.write_frame(frame.into())?;
285 group.finish()?;
286 Ok(())
287 }
288
289 pub fn finish(&mut self) -> Result<()> {
295 let mut state = self.modify()?;
296 if state.final_sequence.is_some() {
297 return Err(Error::Closed);
298 }
299 state.final_sequence = Some(match state.max_sequence {
300 Some(max) => max.checked_add(1).ok_or(coding::BoundsExceeded)?,
301 None => 0,
302 });
303 Ok(())
304 }
305
306 pub fn finish_at(&mut self, sequence: u64) -> Result<()> {
313 let mut state = self.modify()?;
314 let max = state.max_sequence.ok_or(Error::Closed)?;
315 if state.final_sequence.is_some() || sequence != max {
316 return Err(Error::Closed);
317 }
318 state.final_sequence = Some(max.checked_add(1).ok_or(coding::BoundsExceeded)?);
319 Ok(())
320 }
321
322 pub fn abort(&mut self, err: Error) -> Result<()> {
330 let mut guard = self.modify()?;
331 guard.abort = Some(err);
332 guard.groups.clear();
333 guard.duplicates.clear();
334 guard.close();
335 Ok(())
336 }
337
338 pub fn consume(&self) -> TrackConsumer {
340 TrackConsumer {
341 info: self.info.clone(),
342 state: self.state.consume(),
343 index: 0,
344 min_sequence: 0,
345 next_sequence: 0,
346 }
347 }
348
349 pub fn name(&self) -> &str {
351 &self.info.name
352 }
353
354 pub fn demand(&self) -> TrackDemand {
356 TrackDemand {
357 name: self.info.name.clone(),
358 state: self.state.weak(),
359 }
360 }
361
362 pub async fn unused(&self) -> Result<()> {
364 self.state
365 .unused()
366 .await
367 .map_err(|r| r.abort.clone().unwrap_or(Error::Dropped))
368 }
369
370 pub async fn used(&self) -> Result<()> {
372 self.state
373 .used()
374 .await
375 .map_err(|r| r.abort.clone().unwrap_or(Error::Dropped))
376 }
377
378 pub async fn closed(&self) -> Error {
380 self.state.closed().await;
381 self.state.read().abort.clone().unwrap_or(Error::Dropped)
382 }
383
384 pub fn is_closed(&self) -> bool {
386 self.state.read().is_closed()
387 }
388
389 pub fn is_clone(&self, other: &Self) -> bool {
391 self.state.same_channel(&other.state)
392 }
393
394 pub(crate) fn weak(&self) -> TrackWeak {
396 TrackWeak {
397 info: self.info.clone(),
398 state: self.state.weak(),
399 }
400 }
401
402 fn modify(&self) -> Result<kio::Mut<'_, State>> {
403 self.state
404 .write()
405 .map_err(|r| r.abort.clone().unwrap_or(Error::Dropped))
406 }
407}
408
409impl Clone for TrackProducer {
410 fn clone(&self) -> Self {
411 Self {
412 info: self.info.clone(),
413 state: self.state.clone(),
414 }
415 }
416}
417
418impl Drop for TrackProducer {
419 fn drop(&mut self) {
420 if !self.state.is_last() {
425 return;
426 }
427 if let Ok(mut state) = self.state.write()
428 && state.final_sequence.is_none()
429 {
430 state.groups.clear();
431 state.duplicates.clear();
432 }
433 }
434}
435
436impl From<Track> for TrackProducer {
437 fn from(info: Track) -> Self {
438 TrackProducer::new(info)
439 }
440}
441
442#[derive(Clone)]
444pub(crate) struct TrackWeak {
445 pub(crate) info: Track,
446 state: kio::Weak<State>,
447}
448
449#[derive(Clone)]
451pub struct TrackDemand {
452 name: String,
453 state: kio::Weak<State>,
454}
455
456impl TrackDemand {
457 pub fn name(&self) -> &str {
459 &self.name
460 }
461
462 pub async fn used(&self) -> Result<()> {
464 self.state
465 .used()
466 .await
467 .map_err(|r| r.abort.clone().unwrap_or(Error::Dropped))
468 }
469
470 pub async fn unused(&self) -> Result<()> {
472 self.state
473 .unused()
474 .await
475 .map_err(|r| r.abort.clone().unwrap_or(Error::Dropped))
476 }
477
478 pub async fn closed(&self) -> Error {
480 if let Some(state) = self.state.produce() {
481 state.closed().await;
482 }
483
484 self.state.read().abort.clone().unwrap_or(Error::Dropped)
485 }
486}
487
488impl TrackWeak {
489 pub fn is_closed(&self) -> bool {
490 self.state.is_closed()
491 }
492
493 pub fn consume(&self) -> TrackConsumer {
494 TrackConsumer {
495 info: self.info.clone(),
496 state: self.state.consume(),
497 index: 0,
498 min_sequence: 0,
499 next_sequence: 0,
500 }
501 }
502
503 pub async fn unused(&self) -> crate::Result<()> {
504 self.state
505 .unused()
506 .await
507 .map_err(|r| r.abort.clone().unwrap_or(Error::Dropped))
508 }
509
510 pub fn is_clone(&self, other: &Self) -> bool {
511 self.state.same_channel(&other.state)
512 }
513}
514
515#[derive(Clone)]
517pub struct TrackConsumer {
518 info: Track,
519 state: kio::Consumer<State>,
520 index: usize,
522 min_sequence: u64,
524 next_sequence: u64,
527}
528
529impl std::ops::Deref for TrackConsumer {
530 type Target = Track;
531
532 fn deref(&self) -> &Self::Target {
533 &self.info
534 }
535}
536
537impl TrackConsumer {
538 pub fn name(&self) -> &str {
540 &self.info.name
541 }
542
543 fn poll<F, R>(&self, waiter: &kio::Waiter, f: F) -> Poll<Result<R>>
545 where
546 F: Fn(&kio::Ref<'_, State>) -> Poll<Result<R>>,
547 {
548 Poll::Ready(match ready!(self.state.poll(waiter, f)) {
549 Ok(res) => res,
550 Err(state) => Err(state.abort.clone().unwrap_or(Error::Dropped)),
552 })
553 }
554
555 pub fn poll_recv_group(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<GroupConsumer>>> {
566 let Some((consumer, found_index)) =
567 ready!(self.poll(waiter, |state| state.poll_recv_group(self.index, self.min_sequence))?)
568 else {
569 return Poll::Ready(Ok(None));
570 };
571
572 self.index = found_index + 1;
573 Poll::Ready(Ok(Some(consumer)))
574 }
575
576 pub async fn recv_group(&mut self) -> Result<Option<GroupConsumer>> {
582 kio::wait(|waiter| self.poll_recv_group(waiter)).await
583 }
584
585 pub fn poll_next_group(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<GroupConsumer>>> {
591 loop {
592 let Some(group) = ready!(self.poll_recv_group(waiter)?) else {
593 return Poll::Ready(Ok(None));
594 };
595 if group.sequence < self.next_sequence {
596 continue;
598 }
599 self.next_sequence = group.sequence.saturating_add(1);
600 return Poll::Ready(Ok(Some(group)));
601 }
602 }
603
604 pub async fn next_group(&mut self) -> Result<Option<GroupConsumer>> {
610 kio::wait(|waiter| self.poll_next_group(waiter)).await
611 }
612
613 pub fn poll_read_frame(&mut self, waiter: &kio::Waiter) -> Poll<Result<Option<bytes::Bytes>>> {
617 let lower = self.min_sequence.max(self.next_sequence);
618 let Some((frame, found_index, sequence)) =
619 ready!(self.poll(waiter, |state| { state.poll_read_frame(self.index, lower, waiter) })?)
620 else {
621 return Poll::Ready(Ok(None));
622 };
623
624 self.index = found_index + 1;
625 self.next_sequence = sequence.saturating_add(1);
626 Poll::Ready(Ok(Some(frame)))
627 }
628
629 pub async fn read_frame(&mut self) -> Result<Option<bytes::Bytes>> {
633 kio::wait(|waiter| self.poll_read_frame(waiter)).await
634 }
635
636 pub fn poll_get_group(&self, waiter: &kio::Waiter, sequence: u64) -> Poll<Result<Option<GroupConsumer>>> {
638 self.poll(waiter, |state| state.poll_get_group(sequence))
639 }
640
641 pub async fn get_group(&self, sequence: u64) -> Result<Option<GroupConsumer>> {
649 kio::wait(|waiter| self.poll_get_group(waiter, sequence)).await
650 }
651
652 pub fn poll_closed(&self, waiter: &kio::Waiter) -> Poll<Result<()>> {
654 self.poll(waiter, |state| state.poll_closed())
655 }
656
657 pub async fn closed(&self) -> Result<()> {
661 kio::wait(|waiter| self.poll_closed(waiter)).await
662 }
663
664 pub fn is_clone(&self, other: &Self) -> bool {
666 self.state.same_channel(&other.state)
667 }
668
669 pub fn poll_finished(&mut self, waiter: &kio::Waiter) -> Poll<Result<u64>> {
671 self.poll(waiter, |state| state.poll_finished())
672 }
673
674 pub async fn finished(&mut self) -> Result<u64> {
676 kio::wait(|waiter| self.poll_finished(waiter)).await
677 }
678
679 pub fn start_at(&mut self, sequence: u64) {
681 self.min_sequence = sequence;
682 }
683
684 pub fn latest(&self) -> Option<u64> {
686 self.state.read().max_sequence
687 }
688
689 pub(crate) fn weak(&self) -> TrackWeak {
691 TrackWeak {
692 info: self.info.clone(),
693 state: self.state.weak(),
694 }
695 }
696}
697
698#[cfg(test)]
699use futures::FutureExt;
700
701#[cfg(test)]
702impl TrackConsumer {
703 pub fn assert_group(&mut self) -> GroupConsumer {
704 self.recv_group()
705 .now_or_never()
706 .expect("group would have blocked")
707 .expect("would have errored")
708 .expect("track was closed")
709 }
710
711 pub fn assert_no_group(&mut self) {
712 assert!(
713 self.recv_group().now_or_never().is_none(),
714 "recv_group would not have blocked"
715 );
716 }
717
718 pub fn assert_not_closed(&self) {
719 assert!(self.closed().now_or_never().is_none(), "should not be closed");
720 }
721
722 pub fn assert_closed(&self) {
723 assert!(self.closed().now_or_never().is_some(), "should be closed");
724 }
725
726 pub fn assert_error(&self) {
728 assert!(
729 self.closed().now_or_never().expect("should not block").is_err(),
730 "should be error"
731 );
732 }
733
734 pub fn assert_is_clone(&self, other: &Self) {
735 assert!(self.is_clone(other), "should be clone");
736 }
737
738 pub fn assert_not_clone(&self, other: &Self) {
739 assert!(!self.is_clone(other), "should not be clone");
740 }
741}
742
743#[cfg(test)]
744mod test {
745 use super::*;
746
747 fn live_groups(state: &State) -> usize {
749 state.groups.iter().flatten().count()
750 }
751
752 fn first_live_sequence(state: &State) -> u64 {
754 state.groups.iter().flatten().next().unwrap().0.sequence
755 }
756
757 #[tokio::test]
758 async fn evict_expired_groups() {
759 tokio::time::pause();
760
761 let mut producer = Track::new("test").produce();
762
763 producer.append_group().unwrap(); producer.append_group().unwrap(); producer.append_group().unwrap(); {
769 let state = producer.state.read();
770 assert_eq!(live_groups(&state), 3);
771 assert_eq!(state.offset, 0);
772 }
773
774 tokio::time::advance(MAX_GROUP_AGE + Duration::from_secs(1)).await;
776
777 producer.append_group().unwrap(); {
783 let state = producer.state.read();
784 assert_eq!(live_groups(&state), 1);
785 assert_eq!(first_live_sequence(&state), 3);
786 assert_eq!(state.offset, 3);
787 assert!(!state.duplicates.contains(&0));
788 assert!(!state.duplicates.contains(&1));
789 assert!(!state.duplicates.contains(&2));
790 assert!(state.duplicates.contains(&3));
791 }
792 }
793
794 #[tokio::test]
795 async fn evict_keeps_max_sequence() {
796 tokio::time::pause();
797
798 let mut producer = Track::new("test").produce();
799 producer.append_group().unwrap(); tokio::time::advance(MAX_GROUP_AGE + Duration::from_secs(1)).await;
803
804 producer.append_group().unwrap(); {
808 let state = producer.state.read();
809 assert_eq!(live_groups(&state), 1);
810 assert_eq!(first_live_sequence(&state), 1);
811 assert_eq!(state.offset, 1);
812 }
813 }
814
815 #[tokio::test]
816 async fn no_eviction_when_fresh() {
817 tokio::time::pause();
818
819 let mut producer = Track::new("test").produce();
820 producer.append_group().unwrap(); producer.append_group().unwrap(); producer.append_group().unwrap(); {
825 let state = producer.state.read();
826 assert_eq!(live_groups(&state), 3);
827 assert_eq!(state.offset, 0);
828 }
829 }
830
831 #[tokio::test]
832 async fn consumer_skips_evicted_groups() {
833 tokio::time::pause();
834
835 let mut producer = Track::new("test").produce();
836 producer.append_group().unwrap(); let mut consumer = producer.consume();
839
840 tokio::time::advance(MAX_GROUP_AGE + Duration::from_secs(1)).await;
841 producer.append_group().unwrap(); let group = consumer.assert_group();
845 assert_eq!(group.sequence, 1);
846 }
847
848 #[tokio::test]
849 async fn out_of_order_max_sequence_at_front() {
850 tokio::time::pause();
851
852 let mut producer = Track::new("test").produce();
853
854 producer.create_group(Group { sequence: 5 }).unwrap();
856 producer.create_group(Group { sequence: 3 }).unwrap();
857 producer.create_group(Group { sequence: 4 }).unwrap();
858
859 {
861 let state = producer.state.read();
862 assert_eq!(state.max_sequence, Some(5));
863 }
864
865 tokio::time::advance(MAX_GROUP_AGE + Duration::from_secs(1)).await;
867
868 producer.append_group().unwrap(); {
874 let state = producer.state.read();
875 assert_eq!(live_groups(&state), 1);
876 assert_eq!(first_live_sequence(&state), 6);
877 assert!(!state.duplicates.contains(&3));
878 assert!(!state.duplicates.contains(&4));
879 assert!(!state.duplicates.contains(&5));
880 assert!(state.duplicates.contains(&6));
881 }
882 }
883
884 #[tokio::test]
885 async fn max_sequence_at_front_blocks_trim() {
886 tokio::time::pause();
887
888 let mut producer = Track::new("test").produce();
889
890 producer.create_group(Group { sequence: 5 }).unwrap();
892
893 tokio::time::advance(MAX_GROUP_AGE + Duration::from_secs(1)).await;
894
895 producer.create_group(Group { sequence: 3 }).unwrap();
897
898 {
901 let state = producer.state.read();
902 assert_eq!(live_groups(&state), 2);
903 assert_eq!(state.offset, 0);
904 }
905
906 tokio::time::advance(MAX_GROUP_AGE + Duration::from_secs(1)).await;
908
909 producer.create_group(Group { sequence: 2 }).unwrap();
911
912 {
917 let state = producer.state.read();
918 assert_eq!(live_groups(&state), 2);
919 assert_eq!(state.offset, 0);
920 assert!(state.duplicates.contains(&5));
921 assert!(!state.duplicates.contains(&3));
922 assert!(state.duplicates.contains(&2));
923 }
924
925 let mut consumer = producer.consume();
927 let group = consumer.assert_group();
928 assert_eq!(group.sequence, 5);
930 }
931
932 #[tokio::test]
933 async fn abort_clears_cached_groups() {
934 let mut producer = Track::new("test").produce();
935 producer.append_group().unwrap();
936 producer.append_group().unwrap();
937
938 let mut consumer = producer.consume();
940 assert_eq!(live_groups(&producer.state.read()), 2);
941
942 producer.abort(Error::Cancel).unwrap();
943
944 {
945 let state = producer.state.read();
946 assert!(state.groups.is_empty(), "cached groups should be dropped on abort");
947 assert!(state.duplicates.is_empty());
948 }
949
950 let result = consumer.recv_group().now_or_never().expect("should not block");
952 assert!(matches!(result, Err(Error::Cancel)));
953 }
954
955 #[tokio::test]
956 async fn drop_unfinished_clears_cached_groups() {
957 let producer = Track::new("test").produce();
958 let mut writer = producer.clone();
959 writer.append_group().unwrap();
960
961 let mut consumer = producer.consume();
963 assert_eq!(live_groups(&producer.state.read()), 1);
964
965 drop(writer);
967 drop(producer);
968
969 let result = consumer.recv_group().now_or_never().expect("should not block");
970 assert!(matches!(result, Err(Error::Dropped)));
971 }
972
973 #[tokio::test]
974 async fn drop_finished_keeps_cached_groups() {
975 let mut producer = Track::new("test").produce();
976 producer.append_group().unwrap();
977 producer.finish().unwrap();
978
979 let mut consumer = producer.consume();
980 drop(producer);
981
982 assert_eq!(consumer.assert_group().sequence, 0);
984 let done = consumer.recv_group().now_or_never().expect("should not block").unwrap();
985 assert!(done.is_none(), "consumer should drain then see clean finish");
986 }
987
988 #[test]
989 fn append_finish_cannot_be_rewritten() {
990 let mut producer = Track::new("test").produce();
991
992 assert!(producer.finish().is_ok());
994 assert!(producer.finish().is_err());
995 assert!(producer.append_group().is_err());
996 }
997
998 #[test]
999 fn finish_after_groups() {
1000 let mut producer = Track::new("test").produce();
1001
1002 producer.append_group().unwrap();
1003 assert!(producer.finish().is_ok());
1004 assert!(producer.finish().is_err());
1005 assert!(producer.append_group().is_err());
1006 }
1007
1008 #[test]
1009 fn insert_finish_validates_sequence_and_freezes_to_max() {
1010 let mut producer = Track::new("test").produce();
1011 producer.create_group(Group { sequence: 5 }).unwrap();
1012
1013 assert!(producer.finish_at(4).is_err());
1014 assert!(producer.finish_at(10).is_err());
1015 assert!(producer.finish_at(5).is_ok());
1016
1017 {
1018 let state = producer.state.read();
1019 assert_eq!(state.final_sequence, Some(6));
1020 }
1021
1022 assert!(producer.finish_at(5).is_err());
1023 assert!(producer.create_group(Group { sequence: 4 }).is_ok());
1024 assert!(producer.create_group(Group { sequence: 5 }).is_err());
1025 }
1026
1027 #[tokio::test]
1028 async fn recv_group_finishes_without_waiting_for_gaps() {
1029 let mut producer = Track::new("test").produce();
1030 producer.create_group(Group { sequence: 1 }).unwrap();
1031 producer.finish_at(1).unwrap();
1032
1033 let mut consumer = producer.consume();
1034 assert_eq!(consumer.assert_group().sequence, 1);
1035
1036 let done = consumer
1037 .recv_group()
1038 .now_or_never()
1039 .expect("should not block")
1040 .expect("would have errored");
1041 assert!(done.is_none(), "track should finish without waiting for gaps");
1042 }
1043
1044 #[tokio::test]
1045 async fn next_group_skips_late_arrivals() {
1046 let mut producer = Track::new("test").produce();
1047 let mut consumer = producer.consume();
1048
1049 producer.create_group(Group { sequence: 5 }).unwrap();
1051 let group = consumer
1052 .next_group()
1053 .now_or_never()
1054 .expect("should not block")
1055 .expect("would have errored")
1056 .expect("track should not be closed");
1057 assert_eq!(group.sequence, 5);
1058
1059 producer.create_group(Group { sequence: 3 }).unwrap();
1061 producer.create_group(Group { sequence: 4 }).unwrap();
1063 producer.create_group(Group { sequence: 7 }).unwrap();
1065
1066 let group = consumer
1067 .next_group()
1068 .now_or_never()
1069 .expect("should not block")
1070 .expect("would have errored")
1071 .expect("track should not be closed");
1072 assert_eq!(group.sequence, 7);
1073
1074 assert!(
1076 consumer.next_group().now_or_never().is_none(),
1077 "should block waiting for a higher sequence"
1078 );
1079 }
1080
1081 #[tokio::test]
1082 async fn next_group_returns_arrivals_in_order() {
1083 let mut producer = Track::new("test").produce();
1084 let mut consumer = producer.consume();
1085
1086 producer.create_group(Group { sequence: 3 }).unwrap();
1088 producer.create_group(Group { sequence: 5 }).unwrap();
1089
1090 let group = consumer
1091 .next_group()
1092 .now_or_never()
1093 .expect("should not block")
1094 .expect("would have errored")
1095 .expect("track should not be closed");
1096 assert_eq!(group.sequence, 3);
1097
1098 let group = consumer
1099 .next_group()
1100 .now_or_never()
1101 .expect("should not block")
1102 .expect("would have errored")
1103 .expect("track should not be closed");
1104 assert_eq!(group.sequence, 5);
1105 }
1106
1107 #[tokio::test]
1108 async fn recv_group_after_next_group_sees_late_arrivals() {
1109 let mut producer = Track::new("test").produce();
1110 let mut consumer = producer.consume();
1111
1112 producer.create_group(Group { sequence: 5 }).unwrap();
1113 producer.create_group(Group { sequence: 3 }).unwrap();
1114
1115 let group = consumer
1117 .next_group()
1118 .now_or_never()
1119 .expect("should not block")
1120 .expect("would have errored")
1121 .expect("track should not be closed");
1122 assert_eq!(group.sequence, 5);
1123
1124 assert_eq!(consumer.assert_group().sequence, 3);
1127 }
1128
1129 #[tokio::test]
1130 async fn read_frame_returns_single_frame_per_group() {
1131 let mut producer = Track::new("test").produce();
1132 let mut consumer = producer.consume();
1133
1134 producer.write_frame(b"hello".as_slice()).unwrap();
1135 producer.write_frame(b"world".as_slice()).unwrap();
1136
1137 let frame = consumer
1138 .read_frame()
1139 .now_or_never()
1140 .expect("should not block")
1141 .expect("would have errored")
1142 .expect("track should not be closed");
1143 assert_eq!(&frame[..], b"hello");
1144
1145 let frame = consumer
1146 .read_frame()
1147 .now_or_never()
1148 .expect("should not block")
1149 .expect("would have errored")
1150 .expect("track should not be closed");
1151 assert_eq!(&frame[..], b"world");
1152 }
1153
1154 #[tokio::test]
1155 async fn read_frame_skips_stalled_group_for_newer_ready_frame() {
1156 let mut producer = Track::new("test").produce();
1157 let mut consumer = producer.consume();
1158
1159 let _stalled = producer.create_group(Group { sequence: 3 }).unwrap();
1161 let mut g5 = producer.create_group(Group { sequence: 5 }).unwrap();
1163 g5.write_frame(bytes::Bytes::from_static(b"later")).unwrap();
1164 g5.finish().unwrap();
1165
1166 let frame = consumer
1168 .read_frame()
1169 .now_or_never()
1170 .expect("should not block on stalled earlier group")
1171 .expect("would have errored")
1172 .expect("track should not be closed");
1173 assert_eq!(&frame[..], b"later");
1174 }
1175
1176 #[tokio::test]
1177 async fn read_frame_discards_rest_of_multi_frame_group() {
1178 let mut producer = Track::new("test").produce();
1179 let mut consumer = producer.consume();
1180
1181 let mut g0 = producer.create_group(Group { sequence: 0 }).unwrap();
1183 g0.write_frame(bytes::Bytes::from_static(b"one")).unwrap();
1184 g0.write_frame(bytes::Bytes::from_static(b"two")).unwrap();
1185 g0.finish().unwrap();
1186
1187 producer.write_frame(b"next".as_slice()).unwrap();
1189
1190 let frame = consumer
1191 .read_frame()
1192 .now_or_never()
1193 .expect("should not block")
1194 .expect("would have errored")
1195 .expect("track should not be closed");
1196 assert_eq!(&frame[..], b"one");
1197
1198 let frame = consumer
1200 .read_frame()
1201 .now_or_never()
1202 .expect("should not block")
1203 .expect("would have errored")
1204 .expect("track should not be closed");
1205 assert_eq!(&frame[..], b"next");
1206 }
1207
1208 #[tokio::test]
1209 async fn read_frame_waits_for_pending_group_after_finish() {
1210 let mut producer = Track::new("test").produce();
1213 let mut consumer = producer.consume();
1214
1215 let mut g0 = producer.create_group(Group { sequence: 0 }).unwrap();
1216 producer.finish().unwrap();
1217
1218 assert!(
1220 consumer.read_frame().now_or_never().is_none(),
1221 "read_frame must block on a pending group even after finish()"
1222 );
1223
1224 g0.write_frame(bytes::Bytes::from_static(b"late")).unwrap();
1226 let frame = consumer
1227 .read_frame()
1228 .now_or_never()
1229 .expect("should not block once a frame is written")
1230 .expect("would have errored")
1231 .expect("track should not be closed");
1232 assert_eq!(&frame[..], b"late");
1233 }
1234
1235 #[tokio::test]
1236 async fn read_frame_respects_start_at() {
1237 let mut producer = Track::new("test").produce();
1240 let mut consumer = producer.consume();
1241 consumer.start_at(5);
1242
1243 let mut g3 = producer.create_group(Group { sequence: 3 }).unwrap();
1245 g3.write_frame(bytes::Bytes::from_static(b"skip-me")).unwrap();
1246 g3.finish().unwrap();
1247
1248 let mut g5 = producer.create_group(Group { sequence: 5 }).unwrap();
1249 g5.write_frame(bytes::Bytes::from_static(b"keep")).unwrap();
1250 g5.finish().unwrap();
1251
1252 let frame = consumer
1253 .read_frame()
1254 .now_or_never()
1255 .expect("should not block")
1256 .expect("would have errored")
1257 .expect("track should not be closed");
1258 assert_eq!(&frame[..], b"keep");
1259 }
1260
1261 #[tokio::test]
1262 async fn read_frame_returns_none_when_finished() {
1263 let mut producer = Track::new("test").produce();
1264 let mut consumer = producer.consume();
1265
1266 producer.write_frame(b"only".as_slice()).unwrap();
1267 producer.finish().unwrap();
1268
1269 let frame = consumer
1270 .read_frame()
1271 .now_or_never()
1272 .expect("should not block")
1273 .expect("would have errored")
1274 .expect("track should not be closed");
1275 assert_eq!(&frame[..], b"only");
1276
1277 let done = consumer
1278 .read_frame()
1279 .now_or_never()
1280 .expect("should not block")
1281 .expect("would have errored");
1282 assert!(done.is_none());
1283 }
1284
1285 #[tokio::test]
1286 async fn get_group_finishes_without_waiting_for_gaps() {
1287 let mut producer = Track::new("test").produce();
1288 producer.create_group(Group { sequence: 1 }).unwrap();
1289 producer.finish_at(1).unwrap();
1290
1291 let consumer = producer.consume();
1292 assert!(
1294 consumer.get_group(0).now_or_never().is_none(),
1295 "sequence below fin should block (group could still arrive)"
1296 );
1297 assert!(
1298 consumer
1299 .get_group(2)
1300 .now_or_never()
1301 .expect("sequence at-or-after fin should resolve")
1302 .expect("should not error")
1303 .is_none(),
1304 "sequence at-or-after fin should not exist"
1305 );
1306 }
1307
1308 #[test]
1309 fn append_group_returns_bounds_exceeded_on_sequence_overflow() {
1310 let mut producer = Track::new("test").produce();
1311 {
1312 let mut state = producer.state.write().ok().unwrap();
1313 state.max_sequence = Some(u64::MAX);
1314 }
1315
1316 assert!(matches!(producer.append_group(), Err(Error::BoundsExceeded(_))));
1317 }
1318}