1use anyhow::{Context, Result, ensure};2use moq_net::BroadcastProducer;3use sigil_protocol::{GoqCatalogDocument, MAX_MOQ_CATALOG_BYTES};45pub(crate) struct GoqCatalogProducer {6 producer: moq_json::Producer<GoqCatalogDocument>,7}89impl GoqCatalogProducer {10 pub(crate) fn finish(mut self) -> Result<()> {11 self.producer12 .finish()13 .context("finishing Goq catalog track")14 }15}1617pub(crate) fn publish_goq_catalog(broadcast: &mut BroadcastProducer) -> Result<GoqCatalogProducer> {18 let track = broadcast19 .create_track(hang::Catalog::default_track())20 .context("creating catalog.json track")?;21 let mut producer = moq_json::Producer::new(track, moq_json::Config::default());22 let document = GoqCatalogDocument::video_h264();23 document24 .validate()25 .context("validating Goq catalog document")?;26 let snapshot = serde_json::to_vec(&document).context("serializing Goq catalog snapshot")?;27 ensure!(28 snapshot.len() <= MAX_MOQ_CATALOG_BYTES,29 "Goq catalog snapshot exceeds {MAX_MOQ_CATALOG_BYTES} bytes"30 );31 producer32 .update(&document)33 .context("publishing immutable Goq catalog snapshot")?;34 Ok(GoqCatalogProducer { producer })35}363738mod tests {39 use super::*;40 use moq_net::{Broadcast, Track};41 use std::time::Duration;4243 44 async fn catalog_snapshot_is_late_subscribable_and_hang_compatible() {45 let mut broadcast = Broadcast::new().produce();46 let _video = broadcast47 .create_track(Track {48 name: sigil_protocol::MOQ_VIDEO_H264_TRACK.to_owned(),49 priority: sigil_protocol::MOQ_VIDEO_TRACK_PRIORITY,50 })51 .unwrap();52 let catalog = publish_goq_catalog(&mut broadcast).unwrap();5354 let base_track = broadcast55 .consume()56 .subscribe_track(&hang::Catalog::default_track())57 .unwrap();58 let mut base_consumer = moq_json::Consumer::<hang::Catalog>::new(base_track);59 let base = tokio::time::timeout(Duration::from_millis(100), base_consumer.next())60 .await61 .unwrap()62 .unwrap()63 .unwrap();64 assert_eq!(base, hang::Catalog::default());6566 let document_track = broadcast67 .consume()68 .subscribe_track(&hang::Catalog::default_track())69 .unwrap();70 let mut document_consumer = moq_json::Consumer::<GoqCatalogDocument>::new(document_track);71 let document = tokio::time::timeout(Duration::from_millis(100), document_consumer.next())72 .await73 .unwrap()74 .unwrap()75 .unwrap();76 document.validate().unwrap();77 assert_eq!(document, GoqCatalogDocument::video_h264());78 catalog.finish().unwrap();79 }80}