use crate::ebook::archive::ResourceArchive;
use crate::ebook::errors::{ArchiveError, ArchiveResult};
use crate::ebook::resource::{Resource, ResourceContent, ResourceKey};
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::io::Write;
use std::{fs, io};
pub(super) type ArchiveOverlay = HashMap<ArchiveResourceKey, OverlayResource>;
pub(crate) type ResourceKeySet<'a> = HashSet<Cow<'a, ResourceKey<'a>>>;
impl<A> ResourceArchive<A> {
pub(crate) fn remove(&mut self, key: &ResourceKey) -> Option<ResourceContent> {
self.overlay
.remove(key)
.and_then(OverlayResource::take_content)
}
pub(crate) fn insert(
&mut self,
key: ResourceKey,
content: ResourceContent,
) -> Option<ResourceContent> {
let key = ArchiveResourceKey::new(key);
self.overlay
.insert(key, OverlayResource::Content(content))
.and_then(OverlayResource::take_content)
}
pub(crate) fn relocate<'a>(&mut self, current: ResourceKey<'a>, new_key: ResourceKey<'a>) {
let new_key = ArchiveResourceKey::new(new_key);
if let Some(entry) = self.overlay.remove(¤t) {
self.overlay.insert(new_key, entry);
} else {
let current = ArchiveResourceKey::new(current);
self.overlay
.insert(new_key, OverlayResource::Relocated(current.0));
}
}
pub(crate) fn is_overlay_resource(&self, key: &ResourceKey<'_>) -> bool {
self.overlay.contains_key(key)
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub(super) struct ArchiveResourceKey(pub(super) ResourceKey<'static>);
impl ArchiveResourceKey {
fn new(key: ResourceKey<'_>) -> Self {
Self(match key {
ResourceKey::Value(value) => ResourceKey::Value(Cow::Owned(value.into_owned())),
ResourceKey::Position(position) => ResourceKey::Position(position),
})
}
}
impl<'a> std::borrow::Borrow<ResourceKey<'a>> for ArchiveResourceKey {
fn borrow(&self) -> &ResourceKey<'a> {
&self.0
}
}
#[derive(Debug)]
pub(super) enum OverlayResource {
Content(ResourceContent),
Relocated(ResourceKey<'static>),
}
impl OverlayResource {
fn take_content(self) -> Option<ResourceContent> {
match self {
Self::Content(data) => Some(data),
Self::Relocated(_) => None,
}
}
}
impl ResourceContent {
pub(super) fn copy_bytes<W: Write>(
&self,
resource: &Resource<'_>,
mut writer: W,
) -> ArchiveResult<u64> {
match self {
Self::Memory(content) => writer.write_all(content).map(|()| content.len() as u64),
Self::File(path) => {
fs::File::open(path).and_then(|mut file| io::copy(&mut file, &mut writer))
}
}
.map_err(|source| ArchiveError::CannotRead {
resource: resource.as_static(),
source,
})
}
}