labels: expose analyze() for corpus regression tooling
Promotes `mod labels` to `pub mod labels` and adds `analyze()` plus `LabelAnalysis` (both `#[doc(hidden)]`) so an out-of-tree diagnostic binary (freemkv-tools labels-analyze) can introspect which BD-J parser matched a given disc, what JAR files the discriminators saw, and what labels came out — without going through the production `apply()` path that mutates DiscTitles. Also adds `tracing::info!(parser = name, "label parser matched")` / "no label parser matched" inside `extract()`. Dev-only signal: users get the same seamless behavior; developers can finally tell whether a disc hit a real parser or fell through to the codec-name fallback in fill_defaults(). The new `jar_inventory()` helper deduplicates and sorts filenames under any `/BDMV/JAR/<x>/` subdirectory — same plumbing the existing `jar_file_exists()` discriminators use, just enumerated rather than predicate-tested. Used by `analyze()` to surface unrecognized parser-source files when no parser matches, which is the input to "do we need a new parser?" triage. No behavior change to the production label path. `apply()` and `extract()` remain functionally identical; the new public surface exists alongside.
This commit is contained in:
+70
-1
@@ -268,16 +268,85 @@ fn generate_audio_label(
|
||||
}
|
||||
|
||||
fn extract(reader: &mut dyn SectorReader, udf: &UdfFs) -> Vec<StreamLabel> {
|
||||
for (_name, detect, parse) in PARSERS {
|
||||
for (name, detect, parse) in PARSERS {
|
||||
if detect(udf) {
|
||||
tracing::info!(parser = name, "label parser matched");
|
||||
if let Some(labels) = parse(reader, udf) {
|
||||
return labels;
|
||||
}
|
||||
}
|
||||
}
|
||||
tracing::info!("no label parser matched");
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
/// Diagnostic introspection — returns the parser that matched, the
|
||||
/// labels it emitted, and the inventory of files under `/BDMV/JAR/*/`
|
||||
/// that the discriminators looked at. Intended for `freemkv-tools
|
||||
/// labels-analyze` and corpus regression tooling, not production code
|
||||
/// paths. The matching/parsing logic is identical to [`extract`]; only
|
||||
/// the return shape is richer.
|
||||
#[doc(hidden)]
|
||||
pub fn analyze(reader: &mut dyn SectorReader, udf: &UdfFs) -> LabelAnalysis {
|
||||
let inventory = jar_inventory(udf);
|
||||
for (name, detect, parse) in PARSERS {
|
||||
if detect(udf) {
|
||||
tracing::info!(parser = name, "label parser matched");
|
||||
if let Some(labels) = parse(reader, udf) {
|
||||
return LabelAnalysis {
|
||||
parser: Some(name),
|
||||
jar_inventory: inventory,
|
||||
labels,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
tracing::info!("no label parser matched");
|
||||
LabelAnalysis {
|
||||
parser: None,
|
||||
jar_inventory: inventory,
|
||||
labels: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of [`analyze`].
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LabelAnalysis {
|
||||
/// Which parser matched ("paramount" / "criterion" / "pixelogic" /
|
||||
/// "ctrm"), or `None` if no parser matched and the labels code
|
||||
/// would have fallen through to `fill_defaults`.
|
||||
pub parser: Option<&'static str>,
|
||||
/// Filenames found under any `/BDMV/JAR/*/` subdirectory, deduped
|
||||
/// and sorted. Helps spot unknown authoring formats when `parser`
|
||||
/// is `None`.
|
||||
pub jar_inventory: Vec<String>,
|
||||
/// Raw labels emitted by the matched parser (empty if `parser` is
|
||||
/// `None`).
|
||||
pub labels: Vec<StreamLabel>,
|
||||
}
|
||||
|
||||
/// List filenames found under any `/BDMV/JAR/<x>/` subdirectory of
|
||||
/// the disc. Deduped, sorted. Returns an empty vec if no JAR dir is
|
||||
/// present.
|
||||
fn jar_inventory(udf: &UdfFs) -> Vec<String> {
|
||||
let Some(jar_dir) = udf.find_dir("/BDMV/JAR") else {
|
||||
return Vec::new();
|
||||
};
|
||||
let mut out: Vec<String> = Vec::new();
|
||||
for entry in &jar_dir.entries {
|
||||
if entry.is_dir {
|
||||
for child in &entry.entries {
|
||||
if !child.is_dir && !out.contains(&child.name) {
|
||||
out.push(child.name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out.sort();
|
||||
out
|
||||
}
|
||||
|
||||
// ── Shared helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
/// Check if a file exists in any BDMV/JAR subdirectory.
|
||||
|
||||
Reference in New Issue
Block a user