moq_pattern/lib.rs
1//! Exact path patterns for Media over QUIC.
2//!
3//! A [`Pattern`] describes a set of broadcast paths. [`Patterns`] is an unordered union
4//! reduced by exact containment. Matching is linear. [`Pattern::literal`] rejects `*`
5//! because it is reserved for pattern syntax.
6//!
7//! `moq-net` and `moq-auth` re-export this crate. The TypeScript twin is `@moq/pattern`.
8//!
9//! # Grammar
10//!
11//! A pattern is canonical `/`-separated segments:
12//!
13//! - a literal;
14//! - `*`, matching one complete segment;
15//! - `lit*lit`, with one `*` matching bytes inside one segment;
16//! - `**`, matching zero or more complete segments, at most once per pattern.
17//!
18//! Patterns are exact: `foo` matches only `foo`, `foo/**` matches its subtree including
19//! `foo`, `**` matches every path, and the empty pattern matches only the current root.
20//! Parse rejects leading, trailing, or repeated `/`, more than one `*` in a segment,
21//! `**` mixed with literal bytes, more than one `**`, and more than [`Pattern::MAX_SEGMENTS`]
22//! (32) segments. Construction moves `**` before adjacent `*` segments, so `*/**` is
23//! `**/*`.
24//!
25//! # Algebra
26//!
27//! [`Pattern::matches`], [`Pattern::overlaps`], [`Pattern::contains`], [`Pattern::head`],
28//! [`Pattern::specificity`], set-valued [`Pattern::rebase`] and [`Pattern::intersect`],
29//! and [`Pattern::captures`]. A rebase never picks one lossy residual: `**/a` at `a` is
30//! both the empty pattern and `**/a`, and an intersection never picks one lossy
31//! overlap: `a/**` with `**/a` is both `a` and `a/**/a`. A union reduces per member; a
32//! candidate covered only jointly by several members is refused.
33//!
34//! # CAT / C4M
35//!
36//! [Common Access Token](https://shop.cta.tech/products/cta-5007) and
37//! [`draft-ietf-moq-c4m-01`](https://datatracker.ietf.org/doc/draft-ietf-moq-c4m/) match
38//! namespace fields positionally: exact, prefix, or suffix per field, with a trailing
39//! `nil` for exact depth. Without `nil`, longer namespaces that start with the matching
40//! fields are in scope.
41//!
42//! That common subset is:
43//!
44//! | Pattern | C4M |
45//! | --- | --- |
46//! | `foo/bar` | exact `foo`, exact `bar`, `nil` |
47//! | `foo/bar/**` | exact `foo`, exact `bar` (no `nil`) |
48//! | `foo*` | prefix `foo` on that field |
49//! | `*foo` | suffix `foo` on that field |
50//! | `*` | prefix of the empty byte string (any field) |
51//! | `pid/*/chat` | exact `pid`, any field, exact `chat`, `nil` |
52//!
53//! Richer MoQ forms, kept explicit rather than claimed as CAT gaps:
54//!
55//! - `**` not at the end (`**/a`, `a/**/b`): C4M is positional from the front.
56//! - `foo*bar`: C4M's match object is exact, prefix, *or* suffix, not both.
57//!
58//! # Literal paths
59//!
60//! [`Path`](https://docs.rs/moq-net/latest/moq_net/struct.Path.html) stays a coordinate.
61//! Roots, joins, exact names, URL paths, and object-store keys keep their own types.
62//! [`Pattern::literal`] rejects `*`; literal `Path` construction and wire decoding
63//! retain their existing behavior.
64
65#![warn(missing_docs)]
66
67mod pattern;
68mod patterns;
69
70pub use pattern::{IntersectionError, InvalidPattern, Pattern, Segment, Specificity};
71pub use patterns::Patterns;