Skip to main content

broadcast_common/
lib.rs

1//! Shared primitives for the dvb_si / dvb_t2mi / dvb_bbframe family.
2//!
3//! See individual modules for documentation: the [`Parse`] / [`Serialize`]
4//! traits every wire type implements, the MPEG-2 [`crc32_mpeg2`] CRC, the
5//! [`bcd`] / [`time`] codecs, and the [`mux`] container-mux traits.
6//!
7//! # Container-mux traits ([`mux`])
8//!
9//! The [`mux`] module defines the codec-agnostic vocabulary for the any-to-any
10//! muxing hub, mirroring the [`Parse`] / [`Serialize`] symmetry:
11//!
12//! - [`Unpackage`] — demux a packaged container into an in-memory media IR.
13//! - [`Package`] — mux a media IR back into a packaged container.
14//! - [`Decrypt`] / [`Encrypt`] — in-place sample (un)protection.
15//!
16//! Each is generic over its input/output/media/config/key associated types plus
17//! `type Error`; no concrete media or codec types appear here. `Unpackage` ⇄
18//! `Package` and `Decrypt` ⇄ `Encrypt` are inverse pairs. Concrete
19//! implementations live in the container crates (e.g. `transmux`).
20//!
21//! # Quick start
22//! ```
23//! use broadcast_common::{bcd, crc32_mpeg2};
24//!
25//! // Binary-coded decimal (as used in MJD/BCD time fields):
26//! assert_eq!(bcd::from_bcd_byte(0x42), Some(42));
27//! assert_eq!(bcd::to_bcd_byte(42), Some(0x42));
28//!
29//! // MPEG-2 CRC-32 over a section body (deterministic):
30//! let crc = crc32_mpeg2::compute(&[0xDE, 0xAD, 0xBE, 0xEF]);
31//! assert_eq!(crc, crc32_mpeg2::compute(&[0xDE, 0xAD, 0xBE, 0xEF]));
32//! ```
33
34#![forbid(unsafe_code)]
35#![warn(missing_docs)]
36#![cfg_attr(docsrs, feature(doc_cfg))]
37#![cfg_attr(not(feature = "std"), no_std)]
38// The crate's runnable examples, embedded so they render on docs.rs and stay in
39// sync with the actual `examples/*.rs` files (shown, not compiled).
40#![doc = "\n# Examples\n"]
41#![doc = "Two runnable examples ship with this crate (`cargo run -p broadcast-common --example <name>`).\n"]
42#![doc = "\n## `crc_and_bcd`\n\n```rust,ignore"]
43#![doc = include_str!("../examples/crc_and_bcd.rs")]
44#![doc = "```\n\n## `implement_parse_serialize`\n\n```rust,ignore"]
45#![doc = include_str!("../examples/implement_parse_serialize.rs")]
46#![doc = "```"]
47
48extern crate alloc;
49
50pub mod bcd;
51pub mod bits;
52pub mod crc32_mpeg2;
53pub mod mux;
54pub mod time;
55pub mod traits;
56
57pub use mux::{Decrypt, Encrypt, Package, Unpackage};
58pub use traits::{Parse, Serialize};
59
60/// Generate a [`core::fmt::Display`] impl for a spec/field enum that delegates
61/// to an inherent `fn name(&self) -> &'static str`.
62///
63/// This is the project-wide convention for every public spec/field enum across
64/// the `dvb-*` crates (see issue #204): `name()` is the hand-written,
65/// zero-alloc static spec token (lossy on the reserved/unknown arm, which
66/// returns `"reserved"`), and `Display` is the lossless, composable view that
67/// delegates to it. The labels themselves live in `name()` in source — never in
68/// this macro — so they sit next to the variant docs and stay greppable. This
69/// macro carries no labels; it only removes the otherwise-identical `Display`
70/// boilerplate and keeps the two in lockstep.
71///
72/// # Forms
73/// - `impl_spec_display!(Ty)` — every variant's `Display` is exactly `name()`.
74///   Use when there is no byte-bearing catch-all (or its byte need not be
75///   shown), e.g. a unit `Reserved` variant.
76/// - `impl_spec_display!(Ty, Var1, Var2, …)` — each named variant is a
77///   single-field tuple binding a byte; `Display` renders it as
78///   `"{name}(0x{:02X})"` so the value is preserved (e.g. `Reserved(0x1A)` →
79///   `reserved(0x1A)`, `UserDefined(0x1A)` → `user defined(0x1A)`). All other
80///   variants delegate to `name()`.
81///
82/// ```
83/// pub enum Mode { Normal, HighEfficiency, Reserved(u8) }
84/// impl Mode {
85///     pub fn name(&self) -> &'static str {
86///         match self {
87///             Self::Normal => "normal",
88///             Self::HighEfficiency => "high efficiency",
89///             Self::Reserved(_) => "reserved",
90///         }
91///     }
92/// }
93/// broadcast_common::impl_spec_display!(Mode, Reserved);
94/// assert_eq!(Mode::Normal.to_string(), "normal");
95/// assert_eq!(Mode::Reserved(0x1A).to_string(), "reserved(0x1A)");
96/// ```
97#[macro_export]
98macro_rules! impl_spec_display {
99    ($ty:ty) => {
100        impl ::core::fmt::Display for $ty {
101            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
102                f.write_str(self.name())
103            }
104        }
105    };
106    ($ty:ty, $($resv:ident),+ $(,)?) => {
107        impl ::core::fmt::Display for $ty {
108            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
109                match self {
110                    $( Self::$resv(v) => ::core::write!(f, "{}(0x{:02X})", self.name(), v), )+
111                    other => f.write_str(other.name()),
112                }
113            }
114        }
115    };
116}