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>;