#[derive(Debug, Clone)]
pub struct TocElement {
pub level: i32,
pub url: String,
pub title: String,
pub children: Vec<TocElement>,
}
impl TocElement {
pub fn new<S1: Into<String>, S2: Into<String>>(url: S1, title: S2) -> TocElement {
TocElement {
level: 1,
url: url.into(),
title: title.into(),
children: vec![],
}
}
pub fn level(mut self, level: i32) -> Self {
self.level = level;
self
}
fn level_up(&mut self, level: i32) {
self.level = level;
for mut child in &mut self.children {
if child.level <= self.level {
child.level_up(level + 1);
}
}
}
pub fn child(mut self, mut child: TocElement) -> Self {
if child.level <= self.level {
child.level_up(self.level + 1);
}
self.children.push(child);
self
}
pub fn add(&mut self, element: TocElement) {
let mut inserted = false;
if let Some(ref mut last_elem) = self.children.last_mut() {
if element.level > last_elem.level {
last_elem.add(element.clone());
inserted = true;
}
}
if !inserted {
self.children.push(element);
}
}
#[doc(hidden)]
pub fn render_epub(&self, mut offset: u32) -> (u32, String) {
offset += 1;
let id = offset;
let children = if self.children.is_empty() {
String::new()
} else {
let mut output = String::new();
for child in &self.children {
let (n, s) = child.render_epub(offset);
offset = n;
output.push_str(&s);
}
output
};
(offset,
format!("
<navPoint id = \"navPoint-{id}\">
<navLabel>
<text>{title}</text>
</navLabel>
<content src = \"{url}\" />
{children}
</navPoint>",
id = id,
title = self.title,
url = self.url,
children = children))
}
#[doc(hidden)]
pub fn render(&self, numbered: bool) -> String {
if self.title.is_empty() {
return String::new();
}
let children = if self.children.is_empty() {
String::new()
} else {
let mut output = String::new();
for child in &self.children {
output.push_str(&child.render(numbered));
}
format!("\n<{oul}>{children}\n</{oul}>\n",
oul = if numbered { "ol" } else { "ul" },
children = output)
};
format!("<li><a href = \"{link}\">{title}</a>{children}</li>\n",
link = self.url,
title = self.title,
children = children)
}
}
#[derive(Debug, Default)]
pub struct Toc {
pub elements: Vec<TocElement>,
}
impl Toc {
pub fn new() -> Toc {
Toc { elements: vec![] }
}
pub fn is_empty(&self) -> bool {
self.elements.len() <= 1
}
pub fn add(&mut self, element: TocElement) -> &mut Self {
let mut inserted = false;
if let Some(ref mut last_elem) = self.elements.last_mut() {
if element.level > last_elem.level {
last_elem.add(element.clone());
inserted = true;
}
}
if !inserted {
self.elements.push(element);
}
self
}
pub fn render_epub(&mut self) -> String {
let mut output = String::new();
let mut offset = 0;
for elem in &self.elements {
let (n, s) = elem.render_epub(offset);
offset = n;
output.push_str(&s);
}
output
}
pub fn render(&mut self, numbered: bool) -> String {
let mut output = String::new();
for elem in &self.elements {
output.push_str(&elem.render(numbered));
}
format!("<{oul}>\n{output}\n</{oul}>\n",
output = output,
oul = if numbered { "ol" } else { "ul" })
}
}
#[test]
fn toc_simple() {
let mut toc = Toc::new();
toc.add(TocElement::new("#1", "0.0.1").level(3));
toc.add(TocElement::new("#2", "1").level(1));
toc.add(TocElement::new("#3", "1.0.1").level(3));
toc.add(TocElement::new("#4", "1.1").level(2));
toc.add(TocElement::new("#5", "2"));
let actual = toc.render(false);
let expected = "<ul>
<li><a href = \"#1\">0.0.1</a></li>
<li><a href = \"#2\">1</a>
<ul><li><a href = \"#3\">1.0.1</a></li>
<li><a href = \"#4\">1.1</a></li>
</ul>
</li>
<li><a href = \"#5\">2</a></li>
</ul>
";
assert_eq!(&actual, expected);
}
#[test]
fn toc_epub_simple() {
let mut toc = Toc::new();
toc.add(TocElement::new("#1", "1"));
toc.add(TocElement::new("#2", "2"));
let actual = toc.render_epub();
let expected = "
<navPoint id = \"navPoint-1\">
<navLabel>
<text>1</text>
</navLabel>
<content src = \"#1\" />
</navPoint>
<navPoint id = \"navPoint-2\">
<navLabel>
<text>2</text>
</navLabel>
<content src = \"#2\" />
</navPoint>";
assert_eq!(&actual, expected);
}
#[test]
fn toc_epub_simple_sublevels() {
let mut toc = Toc::new();
toc.add(TocElement::new("#1", "1"));
toc.add(TocElement::new("#1.1", "1.1").level(2));
toc.add(TocElement::new("#2", "2"));
toc.add(TocElement::new("#2.1", "2.1").level(2));
let actual = toc.render_epub();
let expected = "
<navPoint id = \"navPoint-1\">
<navLabel>
<text>1</text>
</navLabel>
<content src = \"#1\" />
<navPoint id = \"navPoint-2\">
<navLabel>
<text>1.1</text>
</navLabel>
<content src = \"#1.1\" />
</navPoint>
</navPoint>
<navPoint id = \"navPoint-3\">
<navLabel>
<text>2</text>
</navLabel>
<content src = \"#2\" />
<navPoint id = \"navPoint-4\">
<navLabel>
<text>2.1</text>
</navLabel>
<content src = \"#2.1\" />
</navPoint>
</navPoint>";
assert_eq!(&actual, expected);
}
#[test]
fn toc_epub_broken_sublevels() {
let mut toc = Toc::new();
toc.add(TocElement::new("#1.1", "1.1").level(2));
toc.add(TocElement::new("#2", "2"));
toc.add(TocElement::new("#2.1", "2.1").level(2));
let actual = toc.render_epub();
let expected = "
<navPoint id = \"navPoint-1\">
<navLabel>
<text>1.1</text>
</navLabel>
<content src = \"#1.1\" />
</navPoint>\n<navPoint id = \"navPoint-2\">
<navLabel>
<text>2</text>
</navLabel>
<content src = \"#2\" />
<navPoint id = \"navPoint-3\">
<navLabel>
<text>2.1</text>
</navLabel>
<content src = \"#2.1\" />
</navPoint>\n</navPoint>";
assert_eq!(&actual, expected);
}