aether_renderer_core/
utils.rs1use std::fs::File;
2use std::path::{Path, PathBuf};
3use std::process::Command;
4use tempfile::tempdir;
5use zip::ZipArchive;
6
7pub fn unzip_frames(
10 zip_path: &Path,
11) -> Result<(PathBuf, tempfile::TempDir), Box<dyn std::error::Error>> {
12 let file = File::open(zip_path)
13 .map_err(|e| format!("❌ Failed to open zip file '{}': {}", zip_path.display(), e))?;
14
15 let mut archive =
16 ZipArchive::new(file).map_err(|e| format!("❌ Failed to read zip archive: {}", e))?;
17
18 let temp_dir = tempdir().map_err(|e| format!("❌ Failed to create temp dir: {}", e))?;
19 let temp_path = temp_dir.path().to_path_buf();
20
21 let mut extracted = 0u32;
22 for i in 0..archive.len() {
23 let mut file = archive
24 .by_index(i)
25 .map_err(|e| format!("❌ Failed to access file in zip at index {}: {}", i, e))?;
26
27 let filename = file.name().rsplit('/').next().unwrap_or("");
28 if !filename.ends_with(".png") {
29 continue;
30 }
31
32 let full_out_path = temp_path.join(filename);
33 let mut out_file = File::create(&full_out_path).map_err(|e| {
34 format!(
35 "❌ Failed to create output file '{}': {}",
36 full_out_path.display(),
37 e
38 )
39 })?;
40
41 std::io::copy(&mut file, &mut out_file).map_err(|e| {
42 format!(
43 "❌ Failed to copy content to '{}': {}",
44 full_out_path.display(),
45 e
46 )
47 })?;
48
49 println!("✅ Extracting: {}", full_out_path.display());
50 extracted += 1;
51 }
52
53 if extracted == 0 {
54 return Err("❌ No PNG files found in zip archive".into());
55 }
56
57 println!("🗂️ Extracted frames to: {}", temp_path.display());
58 Ok((temp_path.clone(), temp_dir))
59}
60
61pub fn open_output(path: &str) -> std::io::Result<()> {
63 #[cfg(target_os = "macos")]
64 {
65 Command::new("open").arg(path).status().map(|_| ())
66 }
67 #[cfg(target_os = "linux")]
68 {
69 Command::new("xdg-open").arg(path).status().map(|_| ())
70 }
71 #[cfg(target_os = "windows")]
72 {
73 Command::new("cmd")
74 .args(["/C", "start", path])
75 .status()
76 .map(|_| ())
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::unzip_frames;
83 use std::fs::File;
84 use std::io::Write;
85 use std::path::Path;
86 use tempfile::tempdir;
87 use zip::write::{FileOptions, ZipWriter};
88 use zip::CompressionMethod;
89
90 fn create_test_zip(path: &Path) -> zip::result::ZipResult<()> {
92 let file = File::create(path)?;
93 let mut zip = ZipWriter::new(file);
94 let options = FileOptions::default().compression_method(CompressionMethod::Stored);
95
96 zip.start_file("frame_0000.png", options)?;
97 zip.write_all(b"png0")?;
98 zip.start_file("frame_0001.png", options)?;
99 zip.write_all(b"png1")?;
100 zip.finish()?;
101 Ok(())
102 }
103
104 #[test]
105 fn unzip_frames_extracts_pngs() -> Result<(), Box<dyn std::error::Error>> {
106 let dir = tempdir()?;
107 let zip_path = dir.path().join("frames.zip");
108 create_test_zip(&zip_path)?;
109
110 let (out_dir, _guard) = unzip_frames(&zip_path)?;
111
112 let count = std::fs::read_dir(&out_dir)?.count();
113 assert_eq!(count, 2);
114 assert!(out_dir.join("frame_0000.png").exists());
115 assert!(out_dir.join("frame_0001.png").exists());
116
117 Ok(())
118 }
119}