dvb_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, and the
5//! [`bcd`] / [`time`] codecs.
6//!
7//! # Quick start
8//! ```
9//! use dvb_common::{bcd, crc32_mpeg2};
10//!
11//! // Binary-coded decimal (as used in MJD/BCD time fields):
12//! assert_eq!(bcd::from_bcd_byte(0x42), Some(42));
13//! assert_eq!(bcd::to_bcd_byte(42), Some(0x42));
14//!
15//! // MPEG-2 CRC-32 over a section body (deterministic):
16//! let crc = crc32_mpeg2::compute(&[0xDE, 0xAD, 0xBE, 0xEF]);
17//! assert_eq!(crc, crc32_mpeg2::compute(&[0xDE, 0xAD, 0xBE, 0xEF]));
18//! ```
19
20#![forbid(unsafe_code)]
21#![warn(missing_docs)]
22#![cfg_attr(docsrs, feature(doc_cfg))]
23#![cfg_attr(not(feature = "std"), no_std)]
24
25extern crate alloc;
26
27pub mod bcd;
28pub mod bits;
29pub mod crc32_mpeg2;
30pub mod time;
31pub mod traits;
32
33pub use traits::{Parse, Serialize};
34
35/// Generate a [`core::fmt::Display`] impl for a spec/field enum that delegates
36/// to an inherent `fn name(&self) -> &'static str`.
37///
38/// This is the project-wide convention for every public spec/field enum across
39/// the `dvb-*` crates (see issue #204): `name()` is the hand-written,
40/// zero-alloc static spec token (lossy on the reserved/unknown arm, which
41/// returns `"reserved"`), and `Display` is the lossless, composable view that
42/// delegates to it. The labels themselves live in `name()` in source — never in
43/// this macro — so they sit next to the variant docs and stay greppable. This
44/// macro carries no labels; it only removes the otherwise-identical `Display`
45/// boilerplate and keeps the two in lockstep.
46///
47/// # Forms
48/// - `impl_spec_display!(Ty)` — every variant's `Display` is exactly `name()`.
49/// Use when there is no byte-bearing catch-all (or its byte need not be
50/// shown), e.g. a unit `Reserved` variant.
51/// - `impl_spec_display!(Ty, Var1, Var2, …)` — each named variant is a
52/// single-field tuple binding a byte; `Display` renders it as
53/// `"{name}(0x{:02X})"` so the value is preserved (e.g. `Reserved(0x1A)` →
54/// `reserved(0x1A)`, `UserDefined(0x1A)` → `user defined(0x1A)`). All other
55/// variants delegate to `name()`.
56///
57/// ```
58/// pub enum Mode { Normal, HighEfficiency, Reserved(u8) }
59/// impl Mode {
60/// pub fn name(&self) -> &'static str {
61/// match self {
62/// Self::Normal => "normal",
63/// Self::HighEfficiency => "high efficiency",
64/// Self::Reserved(_) => "reserved",
65/// }
66/// }
67/// }
68/// dvb_common::impl_spec_display!(Mode, Reserved);
69/// assert_eq!(Mode::Normal.to_string(), "normal");
70/// assert_eq!(Mode::Reserved(0x1A).to_string(), "reserved(0x1A)");
71/// ```
72#[macro_export]
73macro_rules! impl_spec_display {
74 ($ty:ty) => {
75 impl ::core::fmt::Display for $ty {
76 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
77 f.write_str(self.name())
78 }
79 }
80 };
81 ($ty:ty, $($resv:ident),+ $(,)?) => {
82 impl ::core::fmt::Display for $ty {
83 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
84 match self {
85 $( Self::$resv(v) => ::core::write!(f, "{}(0x{:02X})", self.name(), v), )+
86 other => f.write_str(other.name()),
87 }
88 }
89 }
90 };
91}