use std::io::Cursor;
use std::path::Path;
use std::sync::OnceLock;
use regxml::{MxfFragmentBuilder, MxfFragmentError, MxfFragmentOptions};
use regxml_dict::{importer::import_registers, MetaDictionary};
use crate::diagnostics::{Location, ValidationIssue};
use crate::mxf::codes::ImfernoMxf;
const REG_ELEMENTS: &[u8] = include_bytes!("../../resources/registers/Elements.xml");
const REG_GROUPS: &[u8] = include_bytes!("../../resources/registers/Groups.xml");
const REG_TYPES: &[u8] = include_bytes!("../../resources/registers/Types.xml");
pub fn dictionaries() -> Option<&'static MetaDictionary> {
static CELL: OnceLock<Option<MetaDictionary>> = OnceLock::new();
CELL.get_or_init(|| import_registers(&[REG_ELEMENTS, REG_GROUPS, REG_TYPES]).ok())
.as_ref()
}
pub fn parse_mxf_to_regxml(
path: &Path,
options: MxfFragmentOptions,
) -> Result<String, MxfFragmentError> {
let dicts = dictionaries().ok_or_else(|| {
MxfFragmentError::Xml(
"imferno metadictionary failed to load — engine misconfigured".to_string(),
)
})?;
let file = std::fs::File::open(path).map_err(|e| MxfFragmentError::Io(e.to_string()))?;
let mut reader = std::io::BufReader::new(file);
let mut buf: Vec<u8> = Vec::new();
MxfFragmentBuilder::from_reader(&mut reader, Cursor::new(&mut buf), dicts, options)?;
String::from_utf8(buf)
.map_err(|e| MxfFragmentError::Xml(format!("RegXML output was not valid UTF-8: {e}")))
}
pub fn regxml_error_issue(path: &Path, err: &MxfFragmentError) -> ValidationIssue {
ValidationIssue::from_code(
ImfernoMxf::RegXmlConversionFailed,
format!(
"Could not convert MXF header metadata of {} to RegXML: {}",
path.display(),
err
),
)
.with_location(Location::new().with_file(path.to_path_buf()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn embedded_dictionaries_load_successfully() {
assert!(
dictionaries().is_some(),
"embedded metadictionaries must parse"
);
}
#[test]
fn parse_mxf_to_regxml_surfaces_io_error_for_missing_file() {
let opts = MxfFragmentOptions::default();
let err = parse_mxf_to_regxml(
std::path::Path::new("/nonexistent/imferno-metadata-test.mxf"),
opts,
)
.expect_err("missing file must error");
let msg = format!("{err}");
assert!(
msg.to_lowercase().contains("no such file")
|| msg.to_lowercase().contains("not found")
|| msg.to_lowercase().contains("os error"),
"expected IO error, got: {msg}"
);
}
}