#[cfg(feature = "write")]
mod write;
use crate::ebook::element::{Attributes, AttributesData, Properties, PropertiesData};
use crate::ebook::resource::Resource;
use crate::ebook::spine::{PageDirection, Spine, SpineEntry};
use crate::epub::manifest::{EpubManifestContext, EpubManifestEntry};
use crate::epub::metadata::{EpubRefinements, EpubRefinementsData};
use crate::epub::package::EpubPackageMetaContext;
use crate::util::{Sealed, doc};
use std::fmt::Debug;
#[cfg(feature = "write")]
pub use write::{DetachedEpubSpineEntry, EpubSpineEntryMut, EpubSpineIterMut, EpubSpineMut};
#[derive(Debug, PartialEq)]
pub(super) struct EpubSpineData {
pub(super) page_direction: PageDirection,
pub(super) entries: Vec<EpubSpineEntryData>,
}
impl EpubSpineData {
pub(super) fn new(page_direction: PageDirection, entries: Vec<EpubSpineEntryData>) -> Self {
Self {
page_direction,
entries,
}
}
pub(super) fn empty() -> Self {
Self {
page_direction: PageDirection::Default,
entries: Vec::new(),
}
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub(super) struct EpubSpineEntryData {
pub(super) id: Option<String>,
pub(super) idref: String,
pub(super) linear: bool,
pub(super) properties: PropertiesData,
pub(super) attributes: AttributesData,
pub(super) refinements: EpubRefinementsData,
}
#[derive(Copy, Clone)]
pub(super) struct EpubSpineContext<'ebook> {
manifest_ctx: EpubManifestContext<'ebook>,
meta_ctx: EpubPackageMetaContext<'ebook>,
}
impl<'ebook> EpubSpineContext<'ebook> {
pub(super) fn new(
manifest_ctx: EpubManifestContext<'ebook>,
meta_ctx: EpubPackageMetaContext<'ebook>,
) -> Self {
Self {
manifest_ctx,
meta_ctx,
}
}
pub(super) fn create_entry(
self,
data: &'ebook EpubSpineEntryData,
index: usize,
) -> EpubSpineEntry<'ebook> {
EpubSpineEntry {
ctx: self,
data,
index,
}
}
}
#[derive(Copy, Clone)]
pub struct EpubSpine<'ebook> {
ctx: EpubSpineContext<'ebook>,
spine: &'ebook EpubSpineData,
}
impl<'ebook> EpubSpine<'ebook> {
pub(super) fn new(
manifest_ctx: EpubManifestContext<'ebook>,
meta_ctx: EpubPackageMetaContext<'ebook>,
spine: &'ebook EpubSpineData,
) -> Self {
Self {
ctx: EpubSpineContext::new(manifest_ctx, meta_ctx),
spine,
}
}
pub fn by_id(&self, id: &str) -> Option<EpubSpineEntry<'ebook>> {
self.spine
.entries
.iter()
.enumerate()
.find(|(_, data)| data.id.as_deref() == Some(id))
.map(|(i, data)| self.ctx.create_entry(data, i))
}
pub fn by_idref(
&self,
idref: &'ebook str,
) -> impl Iterator<Item = EpubSpineEntry<'ebook>> + 'ebook {
let ctx = self.ctx;
self.spine
.entries
.iter()
.enumerate()
.filter(move |(_, data)| data.idref == idref)
.map(move |(i, data)| ctx.create_entry(data, i))
}
#[doc = doc::inherent!(Spine, page_direction)]
pub fn page_direction(&self) -> PageDirection {
self.spine.page_direction
}
#[doc = doc::inherent!(Spine, len)]
pub fn len(&self) -> usize {
self.spine.entries.len()
}
#[doc = doc::inherent!(Spine, is_empty)]
pub fn is_empty(&self) -> bool {
Spine::is_empty(self)
}
#[doc = doc::inherent!(Spine, get)]
pub fn get(&self, index: usize) -> Option<EpubSpineEntry<'ebook>> {
self.spine
.entries
.get(index)
.map(|data| self.ctx.create_entry(data, index))
}
#[doc = doc::inherent!(Spine, iter)]
pub fn iter(&self) -> EpubSpineIter<'ebook> {
EpubSpineIter {
ctx: self.ctx,
iter: self.spine.entries.iter().enumerate(),
}
}
}
impl Sealed for EpubSpine<'_> {}
#[allow(refining_impl_trait)]
impl<'ebook> Spine<'ebook> for EpubSpine<'ebook> {
fn page_direction(&self) -> PageDirection {
self.page_direction()
}
fn len(&self) -> usize {
self.len()
}
fn get(&self, order: usize) -> Option<EpubSpineEntry<'ebook>> {
self.get(order)
}
fn iter(&self) -> EpubSpineIter<'ebook> {
self.iter()
}
}
impl Debug for EpubSpine<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EpubSpine")
.field("data", self.spine)
.finish_non_exhaustive()
}
}
impl PartialEq for EpubSpine<'_> {
fn eq(&self, other: &Self) -> bool {
self.spine == other.spine
}
}
impl<'ebook> IntoIterator for &EpubSpine<'ebook> {
type Item = EpubSpineEntry<'ebook>;
type IntoIter = EpubSpineIter<'ebook>;
fn into_iter(self) -> EpubSpineIter<'ebook> {
self.iter()
}
}
impl<'ebook> IntoIterator for EpubSpine<'ebook> {
type Item = EpubSpineEntry<'ebook>;
type IntoIter = EpubSpineIter<'ebook>;
fn into_iter(self) -> EpubSpineIter<'ebook> {
self.iter()
}
}
pub struct EpubSpineIter<'ebook> {
ctx: EpubSpineContext<'ebook>,
iter: std::iter::Enumerate<std::slice::Iter<'ebook, EpubSpineEntryData>>,
}
impl<'ebook> Iterator for EpubSpineIter<'ebook> {
type Item = EpubSpineEntry<'ebook>;
fn next(&mut self) -> Option<Self::Item> {
self.iter
.next()
.map(|(i, data)| self.ctx.create_entry(data, i))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
#[derive(Copy, Clone)]
pub struct EpubSpineEntry<'ebook> {
ctx: EpubSpineContext<'ebook>,
data: &'ebook EpubSpineEntryData,
index: usize,
}
impl<'ebook> EpubSpineEntry<'ebook> {
pub fn id(&self) -> Option<&'ebook str> {
self.data.id.as_deref()
}
pub fn idref(&self) -> &'ebook str {
&self.data.idref
}
pub fn is_linear(&self) -> bool {
self.data.linear
}
pub fn properties(&self) -> &'ebook Properties {
&self.data.properties
}
pub fn attributes(&self) -> &'ebook Attributes {
&self.data.attributes
}
pub fn refinements(&self) -> EpubRefinements<'ebook> {
self.ctx
.meta_ctx
.create_refinements(self.id(), &self.data.refinements)
}
#[doc = doc::inherent!(SpineEntry, order)]
pub fn order(&self) -> usize {
self.index
}
#[doc = doc::inherent!(SpineEntry, manifest_entry)]
pub fn manifest_entry(&self) -> Option<EpubManifestEntry<'ebook>> {
self.ctx.manifest_ctx.by_id(self.idref())
}
#[doc = doc::inherent!(SpineEntry, resource)]
pub fn resource(&self) -> Option<Resource<'ebook>> {
SpineEntry::resource(self)
}
}
impl Sealed for EpubSpineEntry<'_> {}
#[allow(refining_impl_trait)]
impl<'ebook> SpineEntry<'ebook> for EpubSpineEntry<'ebook> {
fn order(&self) -> usize {
self.order()
}
fn manifest_entry(&self) -> Option<EpubManifestEntry<'ebook>> {
self.manifest_entry()
}
}
impl Debug for EpubSpineEntry<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EpubSpineEntry")
.field("data", self.data)
.finish_non_exhaustive()
}
}
impl PartialEq for EpubSpineEntry<'_> {
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}