1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//! 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",
// 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",
"Output",
"SampleEntryVariant",
"StblChild",
"ProtocolControl",
"AmfValue",
"SgpdEntry",
];
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 mut start = 0;
while let Some(idx) = all[start..].find(&needle) {
let end = start + idx + needle.len();
let next = all[end..].chars().next();
if !matches!(next, Some(c) if c.is_alphanumeric() || c == '_') {
return true;
}
start = end;
}
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."
);
}