transmux 0.24.0

Any-to-any media container muxing hub: demux TS, fMP4/CMAF, MPEG-PS, WebM, FLV, or RTMP into one neutral IR and mux to CMAF/fMP4, progressive MP4, TS, DASH, low-latency DASH, HLS, low-latency HLS, Smooth Streaming, or RTMP. CENC/CBCS encrypt+decrypt, SSAI splice, RTP/RTCP, and an fMP4/CMAF conformance validator; parses codec config headers only, samples stay opaque. no_std + alloc.
Documentation
//! Drift-guard for the spec/field-enum label convention (issue #204),
//! introduced in `transmux` by issue #580.
//!
//! Scans this crate's `src/` for every `pub enum`, subtracts a documented
//! skip-list, and fails if any remaining enum has neither
//! `broadcast_common::impl_spec_display!(Name)` nor a hand-written `Display` impl.
//!
//! Because the project-wide `Display` impl delegates to an inherent
//! `name() -> &'static str`, a present `Display` transitively guarantees
//! `name()` exists (it would not compile otherwise) — so this single coverage
//! check enforces the whole convention and catches the one thing the compiler
//! cannot: a brand-new `pub enum` that nobody labelled.

use std::collections::BTreeSet;
use std::fs;
use std::path::Path;

/// Enums that are intentionally **not** spec/field labels. Each is one of:
/// a structured error; a dispatch/tag enum whose variants wrap full
/// structured payloads (not a flat spec code); a `clap` CLI argument enum; or
/// a data-carrying ADT whose variants hold arbitrary payloads (a static label
/// would be lossy and add nothing — use the typed variant instead).
const SKIP: &[&str] = &[
    // errors (Display comes from `thiserror`'s derive or a hand-written
    // human-readable message, not a spec-token label)
    "Error",
    "FlvError",
    "RtmpError",
    "CliError",
    // `dash_parse::DashParseError` (issue #758 T1): a structured parse error,
    // labelled via a hand-written `Display` impl (matching `FlvError`), not
    // the spec-token convention — already satisfied by that `Display` impl,
    // listed here for the same documentation reason `FlvError`/`RtmpError` are.
    "DashParseError",
    // `smooth_parse::SmoothParseError` (issue #759 T1): a structured parse
    // error, labelled via a hand-written `Display` impl mirroring
    // `DashParseError`, not the spec-token convention — already satisfied by
    // that `Display` impl, listed here for the same documentation reason.
    "SmoothParseError",
    // clap CLI argument enum (labels are the `--format` strings, owned by
    // `clap::ValueEnum`, not this convention)
    "FormatArg",
    // data-carrying ADTs: every variant wraps a distinct structured payload
    // (a full sample-entry/box/config/value type), so a flat label would be
    // lossy — the typed variant *is* the label.
    "CodecConfig",
    "DemuxEvent",
    // `rtp_stream::RtpLossEvent` (issue #779): a data-carrying ADT, same
    // shape as `DemuxEvent` above — each variant wraps distinct structured
    // fields (track/ssrc/sequence numbers), not a flat spec-defined code.
    "RtpLossEvent",
    // `LlHlsSegmenter`'s `Stage::Out` (media plane step 2e-2): a dispatch
    // enum wrapping two full structured payloads (`PartInfo`/`SegmentInfo`),
    // same shape as `DemuxEvent` above — not a spec-defined flat code.
    "LlHlsStageOutput",
    "Output",
    "SampleEntryVariant",
    "StblChild",
    "ProtocolControl",
    "AmfValue",
    "SgpdEntry",
    // `cenc_encrypt` (#564) config enums: `IvGen` carries either a scalar
    // counter base or a full caller-supplied IV list (data-carrying ADT, like
    // `CodecConfig` above); `SubsamplePolicy` is a plain behaviour switch, not
    // a spec-defined wire code, so a `name()`/`Display` label would be
    // fabricated rather than transcribed from a spec table.
    "IvGen",
    "SubsamplePolicy",
];

fn read_rs(dir: &Path, out: &mut Vec<String>) {
    for entry in fs::read_dir(dir).expect("read src dir") {
        let path = entry.expect("dir entry").path();
        if path.is_dir() {
            read_rs(&path, out);
        } else if path.extension().is_some_and(|x| x == "rs") {
            out.push(fs::read_to_string(&path).expect("read .rs"));
        }
    }
}

/// True if `name` appears after `prefix` with an identifier boundary, i.e. the
/// match is the whole enum name and not a longer one sharing the prefix.
fn has_impl(all: &str, prefix: &str, name: &str) -> bool {
    let needle = format!("{prefix}{name}");
    let is_boundary =
        |rest: &str| !matches!(rest.chars().next(), Some(c) if c.is_alphanumeric() || c == '_');
    let is_path_or_space =
        |c: char| c.is_whitespace() || c == ':' || c.is_alphanumeric() || c == '_';

    // Strip a leading `ident::` chain (`crate::`, `broadcast_common::`, …) so a
    // re-exported or fully-qualified invocation still counts as reaching the
    // needle from the very start of the line.
    fn strip_path_qualifier(s: &str) -> &str {
        let mut rest = s;
        loop {
            let ident_len = rest
                .chars()
                .take_while(|c| c.is_alphanumeric() || *c == '_')
                .count();
            if ident_len == 0 {
                break;
            }
            match rest[ident_len..].strip_prefix("::") {
                Some(after) => rest = after,
                None => break,
            }
        }
        rest
    }

    for line in all.lines() {
        let trimmed = line.trim_start();

        // The invocation must be the first non-whitespace token on its line —
        // a commented-out `// impl_spec_display!(...)` no longer satisfies
        // this. A leading crate-path qualifier (`crate::`, `broadcast_common::`)
        // is transparent to this check: it is still the first *statement*.
        if let Some(rest) = strip_path_qualifier(trimmed).strip_prefix(&needle)
            && is_boundary(rest)
        {
            return true;
        }

        // A bare `Display for Name` needle (the generic fallback some crates
        // use) also counts when reached from the `impl` keyword through
        // nothing but a module-path qualifier: `impl ::core::fmt::Display for
        // Name`, `impl std::fmt::Display for Name`, `impl fmt::Display for
        // Name`, `impl Display for Name`.
        if !needle.starts_with("impl")
            && let Some(after_impl) = trimmed.strip_prefix("impl")
            && let Some(pos) = after_impl.find(&needle)
        {
            let qualifier = &after_impl[..pos];
            let rest = &after_impl[pos + needle.len()..];
            if qualifier.chars().all(is_path_or_space) && is_boundary(rest) {
                return true;
            }
        }
    }

    false
}

#[test]
fn every_public_spec_enum_has_a_display_impl() {
    let src = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
    let mut files = Vec::new();
    read_rs(&src, &mut files);
    let all = files.join("\n");

    let mut enums = BTreeSet::new();
    for line in all.lines() {
        if let Some(rest) = line.trim_start().strip_prefix("pub enum ") {
            let name: String = rest
                .chars()
                .take_while(|c| c.is_alphanumeric() || *c == '_')
                .collect();
            if !name.is_empty() {
                enums.insert(name);
            }
        }
    }

    let missing: Vec<_> = enums
        .iter()
        .filter(|e| !SKIP.contains(&e.as_str()))
        .filter(|e| !has_impl(&all, "impl_spec_display!(", e) && !has_impl(&all, "Display for ", e))
        .cloned()
        .collect();

    assert!(
        missing.is_empty(),
        "pub enum(s) missing a Display impl (issue #204 convention): {missing:?}\n\
         Add `broadcast_common::impl_spec_display!(Name)` plus an inherent `name()`, \
         or add the enum to SKIP if it is not a spec/field label."
    );
}