Skip to main content

moq_binary/
lib.rs

1//! Opaque binary payloads over [`moq-net`](moq_net) tracks, in two modes:
2//!
3//! - [`snapshot`]: **lossy**. One value updated over time; a consumer only gets the most recent
4//!   one. Older values are superseded and dropped.
5//! - [`stream`]: **lossless**. An ordered append-log of self-contained payloads, delivered in order
6//!   with nothing superseded. Bounded by the group cache: see [`stream`] for what that costs a
7//!   consumer that falls behind.
8//!
9//! Pick [`snapshot`] when consumers care about "what is the value now" (a poster image, a
10//! serialized state blob) and [`stream`] when they care about every payload (an event log, a
11//! sequence of samples).
12//!
13//! The bytes are opaque: this crate frames them onto a track and optionally compresses them, and
14//! never looks inside. For JSON documents reach for [`moq-json`](https://docs.rs/moq-json) instead,
15//! which adds RFC 7396 merge-patch deltas on top of the same two modes.
16//!
17//! Compression is [`moq-flate`](moq_flate), the same group-scoped DEFLATE moq-json uses, so the two
18//! agree on the wire: each group is one raw DEFLATE stream, sync-flushed at every frame boundary. A
19//! [`stream`] therefore compresses each payload against the earlier ones in its group, while a
20//! [`snapshot`] group holds a single self-contained value.
21
22// The browser transport is `!Send`, so on wasm the shared state behind these `Arc`s is
23// too and clippy suggests `Rc`. The same code is genuinely cross-thread on native, so
24// `Arc` stays and the lint is unactionable here.
25#![cfg_attr(target_arch = "wasm32", allow(clippy::arc_with_non_send_sync))]
26
27pub mod snapshot;
28pub mod stream;
29
30/// How a binary track compresses its frames.
31#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
32pub enum Compression {
33	/// Uncompressed payloads.
34	#[default]
35	None,
36
37	/// Group-scoped raw DEFLATE, sync-flushed at each frame boundary.
38	Deflate,
39}
40
41impl Compression {
42	pub(crate) const fn is_deflate(self) -> bool {
43		matches!(self, Self::Deflate)
44	}
45}
46
47/// Errors produced while publishing or consuming binary payloads.
48#[derive(thiserror::Error, Debug, Clone)]
49#[non_exhaustive]
50pub enum Error {
51	/// An error from the underlying track.
52	#[error(transparent)]
53	Net(#[from] moq_net::Error),
54
55	/// A compressed frame could not be decoded (malformed, truncated, or oversized).
56	#[error(transparent)]
57	Flate(#[from] moq_flate::Error),
58
59	/// A [`stream`] track carried a second group, which a lossless log cannot do.
60	///
61	/// A stream is a single group by construction: a publisher that cannot write a payload ends
62	/// the track rather than rolling. A second group therefore means the records that would have
63	/// completed the first one are gone, so the read fails instead of presenting the remainder as
64	/// a continuous log.
65	#[error("stream rolled to a second group")]
66	Rolled,
67}
68
69/// A [`Result`](std::result::Result) using this crate's [`Error`].
70pub type Result<T> = std::result::Result<T, Error>;