#[derive(Clone, Debug, Default, PartialEq)]
pub struct ElementInfo {
pub name: Option<String>,
pub value: String,
pub measure: Option<String>,
}
#[derive(Clone, Debug, Default)]
pub struct ElementNode {
pub name: String,
pub size: u64,
pub infos: Vec<ElementInfo>,
pub children: Vec<ElementNode>,
pub has_error: bool,
}
impl ElementNode {
pub fn new(name: impl Into<String>) -> Self {
ElementNode {
name: name.into(),
size: 0,
infos: Vec::new(),
children: Vec::new(),
has_error: false,
}
}
}
pub struct ElementTree {
stack: Vec<ElementNode>,
}
impl ElementTree {
pub fn new() -> Self {
ElementTree { stack: vec![ElementNode::new("")] }
}
pub fn element_begin(&mut self, name: impl Into<String>) {
self.stack.push(ElementNode::new(name));
}
pub fn element_end(&mut self) {
if self.stack.len() <= 1 {
return;
}
let child = self.stack.pop().expect("len > 1 checked above");
self.stack.last_mut().expect("len > 0 invariant").children.push(child);
}
pub fn element_name(&mut self, name: impl Into<String>) {
if let Some(last) = self.stack.last_mut() {
last.name = name.into();
}
}
pub fn element_info(&mut self, value: impl Into<String>, measure: Option<&str>) {
if let Some(last) = self.stack.last_mut() {
let value = value.into();
if value == "NOK" || measure == Some("Error") {
last.has_error = true;
}
last.infos.push(ElementInfo { name: None, value, measure: measure.map(String::from) });
}
}
pub fn param(&mut self, name: impl Into<String>, value: impl Into<String>) {
if let Some(last) = self.stack.last_mut() {
last.infos.push(ElementInfo {
name: Some(name.into()),
value: value.into(),
measure: None,
});
}
}
pub fn element_level(&self) -> usize {
self.stack.len().saturating_sub(1)
}
pub fn set_current_size(&mut self, size: u64) {
if let Some(last) = self.stack.last_mut() {
last.size = size;
}
}
pub fn root(&self) -> &ElementNode {
&self.stack[0]
}
pub fn current_mut(&mut self) -> &mut ElementNode {
self.stack.last_mut().expect("stack invariant: root always present")
}
}
impl Default for ElementTree {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_tree_has_root_at_level_0() {
let t = ElementTree::new();
assert_eq!(t.element_level(), 0);
assert_eq!(t.root().children.len(), 0);
}
#[test]
fn begin_end_pair_appends_child_to_root() {
let mut t = ElementTree::new();
t.element_begin("atom");
assert_eq!(t.element_level(), 1);
t.element_end();
assert_eq!(t.element_level(), 0);
assert_eq!(t.root().children.len(), 1);
assert_eq!(t.root().children[0].name, "atom");
}
#[test]
fn nested_begin_end_builds_tree() {
let mut t = ElementTree::new();
t.element_begin("moov");
t.element_begin("trak");
t.element_begin("tkhd");
t.element_end();
t.element_begin("mdia");
t.element_end();
t.element_end();
t.element_end();
let root = t.root();
assert_eq!(root.children.len(), 1);
let moov = &root.children[0];
assert_eq!(moov.name, "moov");
assert_eq!(moov.children.len(), 1);
let trak = &moov.children[0];
assert_eq!(trak.children.len(), 2);
assert_eq!(trak.children[0].name, "tkhd");
assert_eq!(trak.children[1].name, "mdia");
}
#[test]
fn element_info_records_value_and_measure() {
let mut t = ElementTree::new();
t.element_begin("tkhd");
t.element_info("1000", Some("ms"));
t.element_info("42", None);
t.element_end();
let tkhd = &t.root().children[0];
assert_eq!(tkhd.infos.len(), 2);
assert_eq!(tkhd.infos[0].name, None);
assert_eq!(tkhd.infos[0].value, "1000");
assert_eq!(tkhd.infos[0].measure.as_deref(), Some("ms"));
assert_eq!(tkhd.infos[1].measure, None);
}
#[test]
fn param_records_named_field_read() {
let mut t = ElementTree::new();
t.element_begin("mvhd");
t.param("Version", "0");
t.param("Flags", "0x000000");
t.element_end();
let mvhd = &t.root().children[0];
assert_eq!(mvhd.infos.len(), 2);
assert_eq!(mvhd.infos[0].name.as_deref(), Some("Version"));
assert_eq!(mvhd.infos[0].value, "0");
assert_eq!(mvhd.infos[1].name.as_deref(), Some("Flags"));
}
#[test]
fn nok_marks_element_as_error() {
let mut t = ElementTree::new();
t.element_begin("bad");
t.element_info("NOK", None);
t.element_end();
assert!(t.root().children[0].has_error);
}
#[test]
fn measure_error_marks_element_as_error() {
let mut t = ElementTree::new();
t.element_begin("bad");
t.element_info("0x1234", Some("Error"));
t.element_end();
assert!(t.root().children[0].has_error);
}
#[test]
fn element_name_renames_current_frame() {
let mut t = ElementTree::new();
t.element_begin("");
t.element_name("renamed");
t.element_end();
assert_eq!(t.root().children[0].name, "renamed");
}
#[test]
fn extra_element_end_is_noop() {
let mut t = ElementTree::new();
t.element_end();
t.element_end();
assert_eq!(t.element_level(), 0);
}
}