broadcast_common/lib.rs
1#![doc = include_str!("../README.md")]
2#![doc = "\n"]
3//! # Reserved-bit and trailing-byte policy
4//!
5//! One workspace-wide rule for every wire crate (issue #1139):
6//!
7//! - **Parse is tolerant.** A non-conformant reserved bit, or trailing data
8//! that real senders emit, is accepted rather than rejected (a crate may
9//! choose stricter validation, as `dvb-t2mi` does for RFU bits, but must
10//! document it).
11//! - **Serialize preserves what was parsed.** Reserved bits are captured on
12//! parse in typed, spec-named fields and re-emitted verbatim, so a stream
13//! from a sender that deviates from the governing edition re-serializes
14//! **byte-identically** (`dvb-si` descriptors, `mpeg-ps`, `rmt-flute`,
15//! `cc-data` fixed bits, `rtp-packet`'s `rtcp` `profile_extensions`, `mp4-emsg`
16//! flags). There is no raw passthrough blob.
17//! - **Constructed values default to the edition value**: a struct built by
18//! hand (`new(..)` / `Default`) carries `1` for `reserved_future_use` and
19//! H.222.0 "reserved", `0` for `reserved_zero_future_use`, with any explicit
20//! per-field exception documented on the field. (A crate that has not
21//! adopted preserve-on-parse documents which fields it normalizes to the
22//! spec value on serialize; `mpeg-ts`, `mpeg-pes` and `mpeg-ps` preserve.)
23//! - A caller that needs byte-for-byte preservation of reserved bits in a
24//! not-yet-migrated crate **keeps the original bytes** rather than
25//! re-serializing.
26//! - Trailing bytes inside a descriptor or section body are never silently
27//! dropped without a documented decision: hold them in a typed field, or
28//! document the field as "ignored".
29//! - Reserved bytes are always **written explicitly** by `serialize_into`;
30//! never rely on a pre-zeroed output buffer.
31//!
32//! The round-trip gate is byte-exact against the original fixture slice
33//! (see `docs/CRATE-ACCEPTANCE.md`).
34
35#![forbid(unsafe_code)]
36#![warn(missing_docs)]
37#![cfg_attr(docsrs, feature(doc_cfg))]
38#![cfg_attr(not(feature = "std"), no_std)]
39// The crate's runnable examples, embedded so they render on docs.rs and stay in
40// sync with the actual `examples/*.rs` files (shown, not compiled).
41#![doc = "\n# Examples\n"]
42#![doc = "Two runnable examples ship with this crate (`cargo run -p broadcast-common --example <name>`).\n"]
43#![doc = "\n## `crc_and_bcd`\n\n```rust,ignore"]
44#![doc = include_str!("../examples/crc_and_bcd.rs")]
45#![doc = "```\n\n## `implement_parse_serialize`\n\n```rust,ignore"]
46#![doc = include_str!("../examples/implement_parse_serialize.rs")]
47#![doc = "```"]
48
49extern crate alloc;
50
51pub mod backoff;
52#[cfg(feature = "base64")]
53pub mod base64;
54pub mod bcd;
55pub mod ber;
56pub mod bits;
57pub mod cenc;
58pub mod clock33;
59pub mod content_range;
60pub mod crc32_mpeg2;
61pub mod erased;
62pub mod error;
63pub mod hex;
64#[cfg(feature = "std")]
65#[cfg(feature = "http-date")]
66pub mod http_date;
67pub mod klv;
68#[cfg(feature = "lang")]
69pub mod lang;
70pub mod len;
71pub mod mux;
72pub mod parity;
73pub mod pusi;
74pub mod seq;
75pub mod stage;
76pub mod time;
77pub mod timeouts;
78pub mod timescale;
79pub mod traits;
80pub mod units;
81#[cfg(feature = "url")]
82pub mod url_ref;
83#[cfg(feature = "xml")]
84pub mod xml;
85#[cfg(feature = "xml")]
86pub mod xml_chars;
87
88pub use cenc::CencScheme;
89#[cfg(feature = "lang")]
90pub use lang::bcp47_language;
91pub use mux::{Decrypt, Encrypt, Package, Unpackage};
92pub use stage::{Demand, Stage, Timestamp};
93pub use traits::{Parse, Serialize};
94
95/// Generate a [`core::fmt::Display`] impl for a spec/field enum that delegates
96/// to an inherent `fn name(&self) -> &'static str`.
97///
98/// This is the project-wide convention for every public spec/field enum across
99/// the `dvb-*` crates (see issue #204): `name()` is the hand-written,
100/// zero-alloc static spec token (lossy on the reserved/unknown arm, which
101/// returns `"reserved"`), and `Display` is the lossless, composable view that
102/// delegates to it. The labels themselves live in `name()` in source — never in
103/// this macro — so they sit next to the variant docs and stay greppable. This
104/// macro carries no labels; it only removes the otherwise-identical `Display`
105/// boilerplate and keeps the two in lockstep.
106///
107/// # Forms
108/// - `impl_spec_display!(Ty)` — every variant's `Display` is exactly `name()`.
109/// Use when there is no byte-bearing catch-all (or its byte need not be
110/// shown), e.g. a unit `Reserved` variant.
111/// - `impl_spec_display!(Ty, Var1, Var2, …)` — each named variant is a
112/// single-field tuple binding a byte; `Display` renders it as
113/// `"{name}(0x{:02X})"` so the value is preserved (e.g. `Reserved(0x1A)` →
114/// `reserved(0x1A)`, `UserDefined(0x1A)` → `user defined(0x1A)`). All other
115/// variants delegate to `name()`.
116///
117/// ```
118/// pub enum Mode { Normal, HighEfficiency, Reserved(u8) }
119/// impl Mode {
120/// pub fn name(&self) -> &'static str {
121/// match self {
122/// Self::Normal => "normal",
123/// Self::HighEfficiency => "high efficiency",
124/// Self::Reserved(_) => "reserved",
125/// }
126/// }
127/// }
128/// broadcast_common::impl_spec_display!(Mode, Reserved);
129/// assert_eq!(Mode::Normal.to_string(), "normal");
130/// assert_eq!(Mode::Reserved(0x1A).to_string(), "reserved(0x1A)");
131/// ```
132#[macro_export]
133macro_rules! impl_spec_display {
134 ($ty:ty) => {
135 impl ::core::fmt::Display for $ty {
136 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
137 f.write_str(self.name())
138 }
139 }
140 };
141 ($ty:ty, $($resv:ident),+ $(,)?) => {
142 impl ::core::fmt::Display for $ty {
143 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
144 match self {
145 $( Self::$resv(v) => ::core::write!(f, "{}(0x{:02X})", self.name(), v), )+
146 other => f.write_str(other.name()),
147 }
148 }
149 }
150 };
151}