const SAFE_LAYER: &[(&str, &str)] = &[
("frame.rs", include_str!("../src/frame.rs")),
("packet.rs", include_str!("../src/packet.rs")),
("codec.rs", include_str!("../src/codec.rs")),
("codec_context.rs", include_str!("../src/codec_context.rs")),
(
"format_context.rs",
include_str!("../src/format_context.rs"),
),
("scale_context.rs", include_str!("../src/scale_context.rs")),
(
"resample_context.rs",
include_str!("../src/resample_context.rs"),
),
("hwdevice.rs", include_str!("../src/hwdevice.rs")),
("buffersink.rs", include_str!("../src/buffersink.rs")),
("bsf.rs", include_str!("../src/bsf.rs")),
("avcodec.rs", include_str!("../src/avcodec.rs")),
("avformat.rs", include_str!("../src/avformat.rs")),
("swscale.rs", include_str!("../src/swscale.rs")),
(
"swresample/mod.rs",
include_str!("../src/swresample/mod.rs"),
),
(
"swresample/context.rs",
include_str!("../src/swresample/context.rs"),
),
(
"swresample/convert.rs",
include_str!("../src/swresample/convert.rs"),
),
];
fn raw_pointer_public_signatures(src: &str) -> Vec<String> {
const STARTS: [&str; 4] = [
"pub fn ",
"pub unsafe fn ",
"pub const fn ",
"pub async fn ",
];
let lines: Vec<&str> = src.lines().collect();
let mut hits = Vec::new();
let mut i = 0;
while i < lines.len() {
let trimmed = lines[i].trim_start();
if STARTS.iter().any(|s| trimmed.starts_with(s)) {
let mut sig = String::new();
let mut j = i;
loop {
sig.push_str(lines[j]);
sig.push('\n');
if lines[j].contains('{') || lines[j].trim_end().ends_with(';') {
break;
}
j += 1;
if j >= lines.len() {
break;
}
}
let mut exempt = sig.contains("seal-allow-raw");
let mut k = i;
while !exempt && k > 0 {
k -= 1;
let above = lines[k].trim_start();
if above.starts_with("//") || above.starts_with("#[") || above.is_empty() {
if lines[k].contains("seal-allow-raw") {
exempt = true;
}
} else {
break;
}
}
if !exempt && (sig.contains("*const") || sig.contains("*mut")) {
hits.push(sig.trim().to_string());
}
i = j + 1;
} else {
i += 1;
}
}
hits
}
#[test]
fn seal_guard_should_reject_public_raw_pointer_signatures() {
let mut offenders = Vec::new();
for (name, src) in SAFE_LAYER {
for sig in raw_pointer_public_signatures(src) {
offenders.push(format!("{name}:\n{sig}"));
}
}
assert!(
offenders.is_empty(),
"the ff-sys safe layer must expose no raw pointer in a public signature; \
demote the offender(s) to `pub(crate)` (or add a `seal-allow-raw` marker if the \
raw surface is intentionally unsealed):\n\n{}",
offenders.join("\n\n")
);
}
#[test]
fn seal_guard_scanner_should_flag_a_synthetic_raw_signature() {
assert_eq!(
raw_pointer_public_signatures("pub fn bad() -> *mut u8 { core::ptr::null_mut() }").len(),
1,
"a public raw-pointer return must be flagged"
);
assert_eq!(
raw_pointer_public_signatures("pub unsafe fn bad(p: *const u8) -> bool { p.is_null() }")
.len(),
1,
"a public raw-pointer parameter must be flagged"
);
assert_eq!(
raw_pointer_public_signatures("pub fn bad(\n p: *mut u8,\n) -> i32 {\n 0\n}").len(),
1,
"a multi-line public raw-pointer signature must be flagged"
);
assert_eq!(
raw_pointer_public_signatures("pub const fn bad(&self) -> *const u8 { core::ptr::null() }")
.len(),
1,
"a public const-fn raw-pointer return must be flagged"
);
assert!(
raw_pointer_public_signatures("pub(crate) unsafe fn ok(p: *mut u8) {}").is_empty(),
"a pub(crate) raw-pointer signature is sealed and must not be flagged"
);
assert!(
raw_pointer_public_signatures("pub fn ok(x: i32) -> bool { x > 0 }").is_empty(),
"a public non-pointer signature must not be flagged"
);
assert!(
raw_pointer_public_signatures(
"// seal-allow-raw: intentionally unsealed\npub unsafe fn ok(p: *mut u8) {}"
)
.is_empty(),
"a seal-allow-raw-marked signature must not be flagged"
);
assert!(
raw_pointer_public_signatures("pub(super) unsafe fn ok(p: *mut u8) {}").is_empty(),
"a pub(super) raw-pointer signature is sealed and must not be flagged"
);
assert!(
raw_pointer_public_signatures(
"// seal-allow-raw: x\n// continued\n#[must_use]\npub const fn ok(&self) -> *const u8 { core::ptr::null() }"
)
.is_empty(),
"a seal-allow-raw exemption separated by an attribute/comment block must still apply"
);
}