use crate::ebook::element::{Attribute, Attributes, Properties};
use crate::ebook::epub::manifest::EpubManifestContext;
use crate::ebook::epub::metadata::{DetachedEpubMetaEntry, EpubRefinementsMut};
use crate::ebook::epub::package::EpubPackageMetaContext;
use crate::ebook::epub::spine::{
EpubSpine, EpubSpineContext, EpubSpineData, EpubSpineEntry, EpubSpineEntryData,
};
use crate::ebook::spine::PageDirection;
use crate::input::{IntoOption, Many};
use crate::util::iter::IteratorExt;
use std::fmt::Debug;
impl<'ebook> EpubSpineContext<'ebook> {
const EMPTY: EpubSpineContext<'static> = EpubSpineContext {
manifest_ctx: EpubManifestContext::EMPTY,
meta_ctx: EpubPackageMetaContext::EMPTY,
};
fn create(self, data: &'ebook EpubSpineData) -> EpubSpine<'ebook> {
EpubSpine::new(self.manifest_ctx, self.meta_ctx, data)
}
fn create_entry_mut(
self,
data: &'ebook mut EpubSpineEntryData,
index: usize,
) -> EpubSpineEntryMut<'ebook> {
EpubSpineEntryMut::new(self, data, index)
}
}
impl EpubSpineEntry<'_> {
pub fn to_detached(&self) -> DetachedEpubSpineEntry {
DetachedEpubSpineEntry(self.data.clone())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct DetachedEpubSpineEntry(EpubSpineEntryData);
impl DetachedEpubSpineEntry {
pub fn new(idref: impl Into<String>) -> Self {
Self(EpubSpineEntryData {
idref: idref.into(),
..EpubSpineEntryData::default()
})
.linear(true)
}
pub fn as_mut(&mut self) -> EpubSpineEntryMut<'_> {
EpubSpineContext::EMPTY.create_entry_mut(&mut self.0, 0)
}
pub fn as_view(&self) -> EpubSpineEntry<'_> {
EpubSpineContext::EMPTY.create_entry(&self.0, 0)
}
pub fn id(mut self, id: impl IntoOption<String>) -> Self {
self.as_mut().set_id(id);
self
}
pub fn idref(mut self, idref: impl Into<String>) -> Self {
self.as_mut().set_idref(idref);
self
}
pub fn linear(mut self, linear: bool) -> Self {
self.as_mut().set_linear(linear);
self
}
pub fn property(mut self, property: &str) -> Self {
self.as_mut().properties_mut().insert(property);
self
}
pub fn attribute(mut self, attribute: impl Many<Attribute>) -> Self {
self.as_mut().attributes_mut().extend(attribute.iter_many());
self
}
pub fn refinement(mut self, detached: impl Many<DetachedEpubMetaEntry>) -> Self {
self.as_mut().refinements_mut().push(detached);
self
}
}
impl From<&str> for DetachedEpubSpineEntry {
fn from(value: &str) -> Self {
Self::new(value.to_owned())
}
}
impl From<String> for DetachedEpubSpineEntry {
fn from(value: String) -> Self {
Self::new(value)
}
}
impl<'a> From<std::borrow::Cow<'a, str>> for DetachedEpubSpineEntry {
fn from(value: std::borrow::Cow<'a, str>) -> Self {
Self::new(value.into_owned())
}
}
pub struct EpubSpineMut<'ebook> {
ctx: EpubSpineContext<'ebook>,
spine: &'ebook mut EpubSpineData,
}
impl<'ebook> EpubSpineMut<'ebook> {
pub(in crate::ebook::epub) fn new(
ctx: EpubSpineContext<'ebook>,
spine: &'ebook mut EpubSpineData,
) -> Self {
Self { ctx, spine }
}
fn index_of(&mut self, predicate: impl Fn(&EpubSpineEntryData) -> bool) -> Option<usize> {
self.spine.entries.iter().position(predicate)
}
fn insert_detached(
&mut self,
index: usize,
mut detached: impl Iterator<Item = DetachedEpubSpineEntry>,
) {
if detached.has_one_remaining()
&& let Some(entry) = detached.next()
{
self.spine.entries.insert(index, entry.0);
} else {
self.spine
.entries
.splice(index..index, detached.map(|e| e.0));
}
}
pub fn set_page_direction(&mut self, page_direction: PageDirection) -> PageDirection {
std::mem::replace(&mut self.spine.page_direction, page_direction)
}
pub fn push(&mut self, detached: impl Many<DetachedEpubSpineEntry>) {
self.insert(self.spine.entries.len(), detached);
}
pub fn insert(&mut self, index: usize, detached: impl Many<DetachedEpubSpineEntry>) {
self.insert_detached(index, detached.iter_many());
}
pub fn get_mut(&mut self, index: usize) -> Option<EpubSpineEntryMut<'_>> {
self.spine
.entries
.get_mut(index)
.map(|entry| self.ctx.create_entry_mut(entry, index))
}
pub fn by_id_mut(&mut self, id: &str) -> Option<EpubSpineEntryMut<'_>> {
self.iter_mut()
.find(|entry| entry.data.id.as_deref() == Some(id))
}
pub fn by_idref_mut(&mut self, idref: &str) -> impl Iterator<Item = EpubSpineEntryMut<'_>> {
self.iter_mut()
.filter(move |entry| entry.data.idref == idref)
}
pub fn iter_mut(&mut self) -> EpubSpineIterMut<'_> {
EpubSpineIterMut {
ctx: self.ctx,
iter: self.spine.entries.iter_mut().enumerate(),
}
}
pub fn remove(&mut self, index: usize) -> DetachedEpubSpineEntry {
DetachedEpubSpineEntry(self.spine.entries.remove(index))
}
pub fn remove_by_id(&mut self, id: &str) -> Option<DetachedEpubSpineEntry> {
self.index_of(|e| e.id.as_deref() == Some(id))
.map(|i| self.remove(i))
}
pub fn retain(&mut self, mut f: impl FnMut(EpubSpineEntry<'_>) -> bool) {
let mut index = 0;
self.spine.entries.retain(|entry| {
let retain = f(self.ctx.create_entry(entry, index));
index += 1;
retain
});
}
pub fn extract_if(
&mut self,
mut f: impl FnMut(EpubSpineEntry<'_>) -> bool,
) -> impl Iterator<Item = DetachedEpubSpineEntry> {
let ctx = self.ctx;
let mut index = 0;
self.spine
.entries
.extract_if(.., move |e| {
let extract = f(ctx.create_entry(e, index));
index += 1;
extract
})
.map(DetachedEpubSpineEntry)
}
pub fn drain(
&mut self,
range: impl std::ops::RangeBounds<usize>,
) -> impl Iterator<Item = DetachedEpubSpineEntry> {
self.spine.entries.drain(range).map(DetachedEpubSpineEntry)
}
pub fn clear(&mut self) {
self.spine.entries.clear();
}
pub fn as_view(&self) -> EpubSpine<'_> {
self.ctx.create(self.spine)
}
}
impl Debug for EpubSpineMut<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EpubSpineMut")
.field("spine", &self.spine)
.finish_non_exhaustive()
}
}
impl Extend<DetachedEpubSpineEntry> for EpubSpineMut<'_> {
fn extend<T: IntoIterator<Item = DetachedEpubSpineEntry>>(&mut self, iter: T) {
self.insert_detached(self.spine.entries.len(), iter.into_iter());
}
}
impl<'a, 'ebook: 'a> IntoIterator for &'a mut EpubSpineMut<'ebook> {
type Item = EpubSpineEntryMut<'a>;
type IntoIter = EpubSpineIterMut<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
impl<'ebook> IntoIterator for EpubSpineMut<'ebook> {
type Item = EpubSpineEntryMut<'ebook>;
type IntoIter = EpubSpineIterMut<'ebook>;
fn into_iter(self) -> Self::IntoIter {
EpubSpineIterMut {
ctx: self.ctx,
iter: self.spine.entries.iter_mut().enumerate(),
}
}
}
pub struct EpubSpineIterMut<'ebook> {
ctx: EpubSpineContext<'ebook>,
iter: std::iter::Enumerate<std::slice::IterMut<'ebook, EpubSpineEntryData>>,
}
impl Debug for EpubSpineIterMut<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EpubSpineIterMut")
.field("iter", &self.iter)
.finish_non_exhaustive()
}
}
impl<'ebook> Iterator for EpubSpineIterMut<'ebook> {
type Item = EpubSpineEntryMut<'ebook>;
fn next(&mut self) -> Option<Self::Item> {
self.iter
.next()
.map(|(i, entry)| self.ctx.create_entry_mut(entry, i))
}
}
pub struct EpubSpineEntryMut<'ebook> {
ctx: EpubSpineContext<'ebook>,
data: &'ebook mut EpubSpineEntryData,
index: usize,
}
impl<'ebook> EpubSpineEntryMut<'ebook> {
fn new(
ctx: EpubSpineContext<'ebook>,
data: &'ebook mut EpubSpineEntryData,
index: usize,
) -> Self {
Self { ctx, data, index }
}
pub fn set_id(&mut self, id: impl IntoOption<String>) -> Option<String> {
std::mem::replace(&mut self.data.id, id.into_option())
}
pub fn set_idref(&mut self, idref: impl Into<String>) -> String {
std::mem::replace(&mut self.data.idref, idref.into())
}
pub fn set_linear(&mut self, linear: bool) -> bool {
std::mem::replace(&mut self.data.linear, linear)
}
pub fn properties_mut(&mut self) -> &mut Properties {
&mut self.data.properties
}
pub fn attributes_mut(&mut self) -> &mut Attributes {
&mut self.data.attributes
}
pub fn refinements_mut(&mut self) -> EpubRefinementsMut<'_> {
EpubRefinementsMut::new(
self.ctx.meta_ctx,
self.data.id.as_deref(),
&mut self.data.refinements,
)
}
pub fn as_view(&self) -> EpubSpineEntry<'_> {
self.ctx.create_entry(self.data, self.index)
}
}
impl Debug for EpubSpineEntryMut<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EpubSpineEntryMut")
.field("data", &self.data)
.field("index", &self.index)
.finish_non_exhaustive()
}
}