use crate::pipeline::Element;
use std::collections::HashMap;
pub struct ElementGraph {
parent: Vec<Option<usize>>,
children: Vec<Vec<usize>>,
next: Vec<Option<usize>>,
prev: Vec<Option<usize>>,
is_title: Vec<bool>,
}
impl ElementGraph {
pub fn build(elements: &[Element]) -> Self {
let n = elements.len();
let mut parent: Vec<Option<usize>> = vec![None; n];
let mut children: Vec<Vec<usize>> = vec![Vec::new(); n];
let mut next: Vec<Option<usize>> = vec![None; n];
let mut prev: Vec<Option<usize>> = vec![None; n];
let mut is_title: Vec<bool> = vec![false; n];
for i in 0..n {
if i + 1 < n {
next[i] = Some(i + 1);
}
if i > 0 {
prev[i] = Some(i - 1);
}
}
let mut latest_title_for_heading: HashMap<String, usize> = HashMap::new();
for i in 0..n {
if matches!(elements[i], Element::Title(_)) {
is_title[i] = true;
let title_text = elements[i].text().to_string();
latest_title_for_heading.insert(title_text, i);
}
}
let mut active_title_for_heading: HashMap<String, usize> = HashMap::new();
for i in 0..n {
if matches!(elements[i], Element::Title(_)) {
let title_text = elements[i].text().to_string();
active_title_for_heading.insert(title_text, i);
} else {
if let Some(heading_text) = elements[i].metadata().parent_heading.as_deref() {
if let Some(&title_idx) = active_title_for_heading.get(heading_text) {
parent[i] = Some(title_idx);
children[title_idx].push(i);
}
}
}
}
Self {
parent,
children,
next,
prev,
is_title,
}
}
pub fn len(&self) -> usize {
self.parent.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn parent_of(&self, idx: usize) -> Option<usize> {
self.parent[idx]
}
pub fn children_of(&self, idx: usize) -> &[usize] {
&self.children[idx]
}
pub fn next_of(&self, idx: usize) -> Option<usize> {
self.next[idx]
}
pub fn prev_of(&self, idx: usize) -> Option<usize> {
self.prev[idx]
}
pub fn elements_in_section(&self, title_idx: usize) -> Vec<usize> {
self.children_of(title_idx).to_vec()
}
pub fn top_level_sections(&self) -> Vec<usize> {
(0..self.len())
.filter(|&i| self.is_title[i] && self.parent[i].is_none())
.collect()
}
}