use super::emit;
use crate::parse::{Node, VastDocument};
use crate::{DetectedVersion, Issue, Severity, ValidationContext};
static ADTITLE_PLACEHOLDERS: phf::Set<&'static str> = phf::phf_set! {
"ad",
"ads",
"ad 1",
"ad1",
"adtitle",
"ad title",
"title",
"untitled",
"unknown",
"test",
"test ad",
"testing",
"sample",
"sample ad",
"placeholder",
"default",
"demo",
"n/a",
"na",
"none",
"null",
"tbd",
"todo",
"vast ad",
"video ad",
};
static ADSYSTEM_PLACEHOLDERS: phf::Set<&'static str> = phf::phf_set! {
"adsystem",
"ad system",
"adserver",
"ad server",
"server",
"system",
"unknown",
"test",
"testing",
"sample",
"placeholder",
"default",
"demo",
"n/a",
"na",
"none",
"null",
"tbd",
"todo",
};
pub fn check(
doc: &VastDocument,
_version: &DetectedVersion,
ctx: &ValidationContext,
issues: &mut Vec<Issue>,
) {
let Some(vast) = doc.vast_root() else { return };
for (ad_idx, ad) in vast.children_named("Ad").enumerate() {
for kind in ["InLine", "Wrapper"] {
let Some(parent) = ad.child(kind) else {
continue;
};
let parent_path = format!("/VAST/Ad[{}]/{}", ad_idx, kind);
check_adtitle(parent, &parent_path, ctx, issues);
check_adsystem(parent, &parent_path, ctx, issues);
}
}
}
fn check_adtitle(
parent: &Node,
parent_path: &str,
ctx: &ValidationContext,
issues: &mut Vec<Issue>,
) {
let Some(adtitle) = parent.child("AdTitle") else {
return;
};
let normalised = adtitle.text.trim().to_ascii_lowercase();
if normalised.is_empty() || ADTITLE_PLACEHOLDERS.contains(normalised.as_str()) {
emit(
ctx,
issues,
"VAST-2.0-adtitle-quality",
Severity::Warning,
"<AdTitle> value is a placeholder; reporting and ops tooling cannot identify this creative",
Some(format!("{}/AdTitle", parent_path)),
"IAB VAST 2.0 §2",
Some(adtitle),
)
}
}
fn check_adsystem(
parent: &Node,
parent_path: &str,
ctx: &ValidationContext,
issues: &mut Vec<Issue>,
) {
let Some(adsystem) = parent.child("AdSystem") else {
return;
};
let normalised = adsystem.text.trim().to_ascii_lowercase();
if normalised.is_empty() || ADSYSTEM_PLACEHOLDERS.contains(normalised.as_str()) {
emit(
ctx,
issues,
"VAST-2.0-adsystem-quality",
Severity::Info,
"<AdSystem> value is a placeholder; the tag cannot be traced back to its serving system",
Some(format!("{}/AdSystem", parent_path)),
"IAB VAST 2.0 §2",
Some(adsystem),
)
}
if adsystem.attr("version").is_none() {
emit(
ctx,
issues,
"VAST-2.0-adsystem-no-version",
Severity::Info,
"<AdSystem> has no version attribute; the ad server version helps trace provenance in partner discrepancy debugging",
Some(format!("{}/AdSystem", parent_path)),
"IAB VAST 2.0 §2",
Some(adsystem),
)
}
}