use std::collections::{HashMap, HashSet};
use crate::media::MediaBindings;
use crate::{DocumentMetadata, MediaAsset};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ArchiveResource {
pub name: String,
pub archive_id: Option<u32>,
pub content: ArchiveResourceContent,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ArchiveResourceContent {
MediaAsset { media_index: usize },
Opaque { data: Vec<u8> },
}
impl ArchiveResource {
pub fn data<'a>(&'a self, media_assets: &'a [MediaAsset]) -> Option<&'a [u8]> {
match &self.content {
ArchiveResourceContent::MediaAsset { media_index } => media_assets
.get(*media_index)
.filter(|asset| asset.name == self.name)
.map(|asset| asset.data.as_slice()),
ArchiveResourceContent::Opaque { data } => Some(data),
}
}
}
#[derive(Debug)]
pub struct ResolvedArchiveResource<'a> {
pub resource: &'a ArchiveResource,
pub data: &'a [u8],
pub inferred: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ArchiveResourceError {
#[error("media ID {id} has no binding")]
UnboundId { id: u32 },
#[error("media ID {id} has ambiguous bindings")]
AmbiguousBinding { id: u32 },
#[error("media ID {id} names missing archive entry {name}")]
MissingEntry { id: u32, name: String },
#[error("archive entry {name} has ambiguous source files")]
AmbiguousEntry { name: String },
#[error("source bytes for archive entry {name} are unavailable")]
DataUnavailable { name: String },
}
pub struct ArchiveResourceResolver<'a> {
metadata: &'a DocumentMetadata,
bindings: MediaBindings,
resources: HashMap<&'a str, Option<&'a ArchiveResource>>,
}
impl<'a> ArchiveResourceResolver<'a> {
pub(crate) fn new(metadata: &'a DocumentMetadata) -> Self {
let mut resources = HashMap::new();
for resource in &metadata.archive_resources {
resources
.entry(resource.name.as_str())
.and_modify(|entry| *entry = None)
.or_insert(Some(resource));
}
let names: HashSet<_> = resources.keys().map(|name| (*name).to_owned()).collect();
Self {
metadata,
bindings: MediaBindings::new(metadata.media_manifest.as_ref(), &names),
resources,
}
}
pub fn resolve(&self, id: u32) -> Result<ResolvedArchiveResource<'a>, ArchiveResourceError> {
let (name, inferred) = self.bindings.resolve(id)?;
let resource = self.resources.get(name).copied().ok_or_else(|| {
ArchiveResourceError::DataUnavailable {
name: name.to_owned(),
}
})?;
let resource = resource.ok_or_else(|| ArchiveResourceError::AmbiguousEntry {
name: name.to_owned(),
})?;
let data = resource.data(&self.metadata.media_assets).ok_or_else(|| {
ArchiveResourceError::DataUnavailable {
name: name.to_owned(),
}
})?;
Ok(ResolvedArchiveResource {
resource,
data,
inferred,
})
}
}
impl DocumentMetadata {
pub fn archive_resource_resolver(&self) -> ArchiveResourceResolver<'_> {
ArchiveResourceResolver::new(self)
}
pub fn resolve_archive_resource(
&self,
id: u32,
) -> Result<ResolvedArchiveResource<'_>, ArchiveResourceError> {
self.archive_resource_resolver().resolve(id)
}
}