1use std::io::Write;
2
3use zip::{CompressionMethod, ZipWriter, write::SimpleFileOptions};
4
5use crate::{compile_html::UnpackedPresentation, error::VecslideError};
6
7pub fn pack_to_writer<W: Write + std::io::Seek>(
16 unpacked: &UnpackedPresentation,
17 writer: W,
18) -> Result<(), VecslideError> {
19 let mut zip = ZipWriter::new(writer);
20
21 let deflated = SimpleFileOptions::default().compression_method(CompressionMethod::Deflated);
22 let stored = SimpleFileOptions::default().compression_method(CompressionMethod::Stored);
23
24 let yaml = serde_norway::to_string(&unpacked.manifest)?;
26 zip.start_file("manifest.yaml", deflated)?;
27 zip.write_all(yaml.as_bytes())?;
28
29 for (path, bytes) in &unpacked.svgs {
31 zip.start_file(path.as_str(), deflated)?;
32 zip.write_all(bytes)?;
33 }
34
35 for (path, bytes) in &unpacked.extra_files {
37 zip.start_file(path.as_str(), deflated)?;
38 zip.write_all(bytes)?;
39 }
40
41 if !unpacked.audio.is_empty()
43 && let Some(ref track) = unpacked.manifest.audio_track
44 {
45 zip.start_file(track.as_str(), stored)?;
46 zip.write_all(&unpacked.audio)?;
47 }
48
49 zip.finish()?;
50 Ok(())
51}
52
53#[cfg(feature = "native")]
62pub fn pack(
63 source_dir: &std::path::Path,
64 manifest: &crate::manifest::Presentation,
65 output: &std::path::Path,
66) -> Result<(), VecslideError> {
67 use std::{
68 fs::File,
69 io::{BufWriter, Read},
70 };
71
72 let file = File::create(output)?;
73 let mut zip = ZipWriter::new(BufWriter::new(file));
74
75 let deflated = SimpleFileOptions::default().compression_method(CompressionMethod::Deflated);
76 let stored = SimpleFileOptions::default().compression_method(CompressionMethod::Stored);
77
78 let yaml = serde_norway::to_string(manifest)?;
80 zip.start_file("manifest.yaml", deflated)?;
81 zip.write_all(yaml.as_bytes())?;
82
83 let mut written: std::collections::HashSet<String> = std::collections::HashSet::new();
85 written.insert("manifest.yaml".to_string());
86
87 for slide in &manifest.slides {
89 if let Some(ref svg_file) = slide.svg_file {
90 if written.contains(svg_file.as_str()) {
91 continue;
92 }
93 let svg_path = source_dir.join(svg_file);
94 let mut buf = Vec::new();
95 File::open(&svg_path)?.read_to_end(&mut buf)?;
96
97 zip.start_file(svg_file.as_str(), deflated)?;
98 zip.write_all(&buf)?;
99 written.insert(svg_file.clone());
100 }
101 }
102
103 if let Some(ref typst_source) = manifest.typst_source
105 && !written.contains(typst_source.as_str())
106 {
107 let path = source_dir.join(typst_source);
108 let mut buf = Vec::new();
109 File::open(&path)?.read_to_end(&mut buf)?;
110
111 zip.start_file(typst_source.as_str(), deflated)?;
112 zip.write_all(&buf)?;
113 written.insert(typst_source.clone());
114 }
115
116 for slide in &manifest.slides {
118 if let Some(ref typst_file) = slide.typst_file {
119 if written.contains(typst_file.as_str()) {
120 continue;
121 }
122 let path = source_dir.join(typst_file);
123 let mut buf = Vec::new();
124 File::open(&path)?.read_to_end(&mut buf)?;
125
126 zip.start_file(typst_file.as_str(), deflated)?;
127 zip.write_all(&buf)?;
128 written.insert(typst_file.clone());
129 }
130 }
131
132 if let Some(ref track) = manifest.audio_track {
135 let audio_path = source_dir.join(track);
136 let mut audio_buf = Vec::new();
137 File::open(&audio_path)?.read_to_end(&mut audio_buf)?;
138 zip.start_file(track.as_str(), stored)?;
139 zip.write_all(&audio_buf)?;
140 written.insert(track.clone());
141 }
142
143 zip.finish()?;
144 Ok(())
145}