use std::{
collections::{HashMap, HashSet},
io::{Read, Seek},
};
use zip::ZipArchive;
use crate::{
compile_html::UnpackedPresentation,
error::VecslideError,
manifest::Presentation,
};
pub fn unpack_from_reader<R: Read + Seek>(reader: R) -> Result<UnpackedPresentation, VecslideError> {
let mut archive = ZipArchive::new(reader)?;
let manifest: Presentation = {
let mut entry = archive.by_name("manifest.yaml")?;
let mut yaml = String::new();
entry.read_to_string(&mut yaml)?;
serde_norway::from_str(&yaml)?
};
let known_svgs: HashSet<&str> = manifest
.slides
.iter()
.filter_map(|s| s.svg_file.as_deref())
.collect();
let audio_path: Option<&str> = manifest.audio_track.as_deref();
let audio = if let Some(track) = audio_path {
let mut entry = archive.by_name(track)?;
let mut buf = Vec::new();
entry.read_to_end(&mut buf)?;
buf
} else {
Vec::new()
};
let mut svgs = HashMap::new();
for slide in &manifest.slides {
if let Some(ref svg_file) = slide.svg_file {
if svgs.contains_key(svg_file) {
continue;
}
let mut entry = archive.by_name(svg_file)?;
let mut buf = Vec::new();
entry.read_to_end(&mut buf)?;
svgs.insert(svg_file.clone(), buf);
}
}
let mut extra_files = HashMap::new();
for i in 0..archive.len() {
let mut entry = archive.by_index(i)?;
let name = entry.name().to_string();
if name == "manifest.yaml"
|| known_svgs.contains(name.as_str())
|| audio_path == Some(name.as_str())
{
continue;
}
let mut buf = Vec::new();
entry.read_to_end(&mut buf)?;
extra_files.insert(name, buf);
}
Ok(UnpackedPresentation { manifest, svgs, extra_files, audio, theme_css: None })
}
#[cfg(feature = "native")]
pub fn unpack_to_dir(
input: &std::path::Path,
output_dir: &std::path::Path,
) -> Result<Presentation, VecslideError> {
use std::fs;
let file = std::fs::File::open(input)?;
let mut archive = ZipArchive::new(file)?;
fs::create_dir_all(output_dir)?;
for i in 0..archive.len() {
let mut entry = archive.by_index(i)?;
let entry_path = output_dir.join(entry.name());
if entry.is_dir() {
fs::create_dir_all(&entry_path)?;
} else {
if let Some(parent) = entry_path.parent() {
fs::create_dir_all(parent)?;
}
let mut out = fs::File::create(&entry_path)?;
std::io::copy(&mut entry, &mut out)?;
}
}
let manifest_path = output_dir.join("manifest.yaml");
let yaml = fs::read_to_string(manifest_path)?;
let manifest: Presentation = serde_norway::from_str(&yaml)?;
Ok(manifest)
}
#[cfg(feature = "native")]
pub fn unpack(input: &std::path::Path) -> Result<UnpackedPresentation, VecslideError> {
let file = std::fs::File::open(input)?;
unpack_from_reader(file)
}