use crate::model::{Chapter, Node, NodeId, Role};
pub fn normalize_table_structure(chapter: &mut Chapter) {
let tables: Vec<NodeId> = (0..chapter.node_count())
.filter_map(|i| {
let id = NodeId(i as u32);
chapter.node(id).and_then(|node| {
if node.role == Role::Table {
Some(id)
} else {
None
}
})
})
.collect();
for table_id in tables {
normalize_single_table(chapter, table_id);
}
}
fn normalize_single_table(chapter: &mut Chapter, table_id: NodeId) {
let has_section_wrapper = chapter.children(table_id).any(|child_id| {
chapter
.node(child_id)
.map(|n| matches!(n.role, Role::TableHead | Role::TableBody))
.unwrap_or(false)
});
if has_section_wrapper {
return;
}
let mut header_rows: Vec<NodeId> = Vec::new();
let mut body_rows: Vec<NodeId> = Vec::new();
for row_id in chapter.children(table_id).collect::<Vec<_>>() {
let is_header_row = is_table_header_row(chapter, row_id);
if is_header_row {
header_rows.push(row_id);
} else {
body_rows.push(row_id);
}
}
if let Some(table) = chapter.node_mut(table_id) {
table.first_child = None;
}
if !body_rows.is_empty() {
let body_id = chapter.alloc_node(Node::new(Role::TableBody));
if let Some(body) = chapter.node_mut(body_id) {
body.first_child = Some(body_rows[0]);
}
for i in 0..body_rows.len() {
if let Some(row) = chapter.node_mut(body_rows[i]) {
row.next_sibling = body_rows.get(i + 1).copied();
}
}
if let Some(table) = chapter.node_mut(table_id) {
table.first_child = Some(body_id);
}
}
if !header_rows.is_empty() {
let head_id = chapter.alloc_node(Node::new(Role::TableHead));
if let Some(head) = chapter.node_mut(head_id) {
head.first_child = Some(header_rows[0]);
}
for i in 0..header_rows.len() {
if let Some(row) = chapter.node_mut(header_rows[i]) {
row.next_sibling = header_rows.get(i + 1).copied();
}
}
let current_first = chapter.node(table_id).and_then(|n| n.first_child);
if let Some(head) = chapter.node_mut(head_id) {
head.next_sibling = current_first;
}
if let Some(table) = chapter.node_mut(table_id) {
table.first_child = Some(head_id);
}
}
if header_rows.is_empty() && body_rows.is_empty() {
let body_id = chapter.alloc_node(Node::new(Role::TableBody));
if let Some(table) = chapter.node_mut(table_id) {
table.first_child = Some(body_id);
}
}
}
fn is_table_header_row(chapter: &Chapter, row_id: NodeId) -> bool {
let mut has_cells = false;
let mut all_header = true;
for cell_id in chapter.children(row_id) {
if let Some(cell) = chapter.node(cell_id)
&& cell.role == Role::TableCell
{
has_cells = true;
if !chapter.semantics.is_header_cell(cell_id) {
all_header = false;
break;
}
}
}
has_cells && all_header
}