use crate::epub::consts::{epub, xhtml, xhtml::bytes, xml};
use crate::epub::metadata::EpubVersion;
use crate::epub::parser::EpubParserValidator;
use crate::epub::parser::toc::TocParser;
use crate::epub::toc::{EpubTocEntryData, TocGroups};
use crate::parser::ParserResult;
use crate::parser::xml::{XmlEvent, XmlStartElement, extract_attributes};
impl<'a> TocParser<'_, 'a> {
pub(super) fn parse_epub3_nav(mut self) -> ParserResult<TocGroups> {
let mut next_event = None;
while let Some(event) = self.reader.take_or_next(&mut next_event) {
match event? {
XmlEvent::Start(el) => match el.local_name() {
bytes::NAV => self.push_nav_root(&el)?,
bytes::LIST_ITEM => next_event = self.push_nav_child(&el)?,
bytes::ANCHOR => self.handle_nav_anchor(&el)?,
_ => {}
},
XmlEvent::End(el) => match el.local_name().as_ref() {
bytes::NAV | bytes::LIST_ITEM => {
self.handle_pop(EpubVersion::EPUB3);
}
_ => {}
},
_ => {}
}
}
Ok(self.groups)
}
fn push_nav_root(&mut self, el: &XmlStartElement<'_>) -> ParserResult<()> {
extract_attributes! {
el.attributes(),
epub::bytes::TYPE => epub_type,
xml::bytes::ID => id,
..remaining,
}
let mut epub_type = self.require_attribute(epub_type, "nav[*epub:type]")?;
epub_type.shrink_to(epub_type.find(' ').unwrap_or(epub_type.len()));
let (_, label) = self
.reader
.get_text_till_either(el.name(), xhtml::ORDERED_LIST.as_bytes())?;
self.stack.push(EpubTocEntryData {
attributes: remaining.into(),
kind: Some(epub_type),
id,
label,
..EpubTocEntryData::default()
});
Ok(())
}
fn push_nav_child(&mut self, el: &XmlStartElement<'_>) -> ParserResult<Option<XmlEvent<'a>>> {
extract_attributes! {
el.attributes(),
xml::bytes::ID => id,
..remaining,
}
let (consumed_event, label) = self
.reader
.get_text_till_either(el.name(), xhtml::ANCHOR.as_bytes())?;
self.stack.push(EpubTocEntryData {
attributes: remaining.into(),
id,
label,
..EpubTocEntryData::default()
});
Ok(consumed_event)
}
fn handle_nav_anchor(&mut self, el: &XmlStartElement) -> ParserResult<()> {
if let Some(nav_entry) = self.stack.last_mut() {
extract_attributes! {
el.attributes(),
bytes::HREF => href_raw,
xml::bytes::ID => id,
epub::bytes::TYPE => epub_type,
}
self.ctx.check_attribute(&href_raw, "a[*href]")?;
let href = href_raw
.as_deref()
.map(|raw| self.ctx.require_href(self.resolver.resolve(raw)))
.transpose()?;
nav_entry.id = id;
nav_entry.href = href;
nav_entry.href_raw = href_raw;
nav_entry.kind = epub_type;
nav_entry.label = self.reader.get_element_text(el)?;
}
Ok(())
}
}