use crate::ebook::manifest::ManifestEntry;
use crate::ebook::resource::Resource;
use crate::ebook::toc::macros::toc_entry_kind;
use crate::util::Sealed;
use std::fmt::Display;
pub trait Toc<'ebook>: Sealed {
fn contents(&self) -> Option<impl TocEntry<'ebook> + 'ebook>;
fn by_kind(
&self,
kind: impl Into<TocEntryKind<'ebook>>,
) -> Option<impl TocEntry<'ebook> + 'ebook>;
fn iter(&self) -> impl Iterator<Item = impl TocEntry<'ebook>> + 'ebook;
}
pub trait TocEntry<'ebook>: Sealed {
fn depth(&self) -> usize;
fn label(&self) -> &'ebook str;
fn kind(&self) -> TocEntryKind<'ebook>;
fn manifest_entry(&self) -> Option<impl ManifestEntry<'ebook> + 'ebook>;
fn resource(&self) -> Option<Resource<'ebook>> {
self.manifest_entry().map(|entry| entry.resource())
}
fn get(&self, index: usize) -> Option<impl TocEntry<'ebook> + 'ebook>;
fn iter(&self) -> impl Iterator<Item = impl TocEntry<'ebook> + 'ebook> + 'ebook;
fn flatten(&self) -> impl Iterator<Item = impl TocEntry<'ebook> + 'ebook> + 'ebook;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn is_root(&self) -> bool {
self.depth() == 0
}
fn max_depth(&self) -> usize {
self.iter()
.fold(0, |depth, child| depth.max(1 + child.max_depth()))
}
fn total_len(&self) -> usize {
self.iter()
.fold(0, |total, child| total + child.total_len() + 1)
}
}
toc_entry_kind! {
Acknowledgments => "acknowledgments",
Afterword => "afterword",
Appendix => "appendix",
BackMatter => "backmatter",
Bibliography => "bibliography",
BodyMatter => "bodymatter" | "text",
Chapter => "chapter",
Colophon => "colophon",
Conclusion => "conclusion",
Contributors => "contributors",
CopyrightPage => "copyright-page" | "copyright",
Cover => "cover",
Dedication => "dedication",
Endnotes => "endnotes",
Epigraph => "epigraph",
Epilogue => "epilogue",
Errata => "errata",
Footnotes => "footnotes",
Foreword => "foreword",
FrontMatter => "frontmatter",
Glossary => "glossary",
Imprint => "imprint",
Index => "index",
Introduction => "introduction",
Landmarks => "landmarks",
ListOfIllustrations => "loi",
ListOfAudio => "loa",
ListOfTables => "lot",
ListOfVideos => "lov",
PageList => "page-list",
Part => "part",
Preamble => "preamble",
Preface => "preface",
Prologue => "prologue",
Qna => "qna",
TitlePage => "titlepage" | "title-page",
Toc => "toc",
Volume => "volume",
}
mod macros {
macro_rules! toc_entry_kind {
{
$($map_enum:ident => $map_string:literal $(| $additional_mapping:literal)*,)*
} => {
#[non_exhaustive]
#[derive(Copy, Clone, Debug, Default, Hash, PartialEq, Eq)]
pub enum TocEntryKind<'ebook> {
$(
#[doc = concat!("Maps to `", $map_string, "`.")]
$(
#[doc = concat!("- `", $additional_mapping, "` → `", $map_string, "`")]
)*
#[doc = concat!("<https://www.w3.org/TR/epub-ssv-11/#", $map_string, ">.")]
#[doc = concat!(
" # use rbook::ebook::toc::TocEntryKind::{self, ",
stringify!($map_enum),
"};"
)]
#[doc = concat!(
"assert_eq!(TocEntryKind::",
stringify!($map_enum),
", TocEntryKind::from(\"",
$map_string,
"\"))"
)]
$map_enum,
)*
#[default]
Unknown,
Other(&'ebook str),
}
impl TocEntryKind<'_> {
pub fn as_str(&self) -> &str {
match self {
$(Self::$map_enum => $map_string,)*
Self::Unknown => "unknown",
Self::Other(value) => value,
}
}
}
impl<'ebook, S: AsRef<str> + ?Sized> From<&'ebook S> for TocEntryKind<'ebook> {
fn from(value: &'ebook S) -> Self {
let value = value.as_ref();
match value {
$($map_string $(| $additional_mapping)* => Self::$map_enum,)*
"" => Self::Unknown,
_ => Self::Other(value)
}
}
}
impl<'ebook> From<&'ebook Self> for TocEntryKind<'ebook> {
fn from(value: &'ebook Self) -> Self {
match value {
$(Self::$map_enum => Self::$map_enum,)*
Self::Unknown => Self::Unknown,
Self::Other(other) => Self::Other(other)
}
}
}
impl Display for TocEntryKind<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
};
}
pub(super) use toc_entry_kind;
}