Move the bundle export to zip 9

30a56d2 · Gabriel Morell · 2026-10-23 05:38

6 files +86 -35
Message
{commit_body(@commit)}

Files changed

modified Cargo.lock
+26 −30
@@ -161,15 +161,6 @@ version = "0.2.2"
161 161 source = "registry+https://github.com/rust-lang/crates.io-index"
162 162 checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
163 163
164 −[[package]]
165 −name = "arbitrary"
166 −version = "1.4.2"
167 −source = "registry+https://github.com/rust-lang/crates.io-index"
168 −checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
169 −dependencies = [
170 − "derive_arbitrary",
171 −]
172 −
173 164 [[package]]
174 165 name = "arboard"
175 166 version = "3.6.1"
@@ -841,17 +832,6 @@ dependencies = [
841 832 "syn 1.0.109",
842 833 ]
843 834
844 −[[package]]
845 −name = "derive_arbitrary"
846 −version = "1.4.2"
847 −source = "registry+https://github.com/rust-lang/crates.io-index"
848 −checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
849 −dependencies = [
850 − "proc-macro2",
851 − "quote",
852 − "syn 2.0.119",
853 −]
854 −
855 835 [[package]]
856 836 name = "digest"
857 837 version = "0.10.7"
@@ -2012,6 +1992,18 @@ dependencies = [
2012 1992 "memoffset 0.7.1",
2013 1993 ]
2014 1994
1995 +[[package]]
1996 +name = "nix"
1997 +version = "0.31.3"
1998 +source = "registry+https://github.com/rust-lang/crates.io-index"
1999 +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
2000 +dependencies = [
2001 + "bitflags 2.13.2",
2002 + "cfg-if",
2003 + "cfg_aliases",
2004 + "libc",
2005 +]
2006 +
2015 2007 [[package]]
2016 2008 name = "nohash-hasher"
2017 2009 version = "0.2.0"
@@ -2044,7 +2036,7 @@ version = "0.7.6"
2044 2036 source = "registry+https://github.com/rust-lang/crates.io-index"
2045 2037 checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
2046 2038 dependencies = [
2047 − "proc-macro-crate 1.3.1",
2039 + "proc-macro-crate 3.5.0",
2048 2040 "proc-macro2",
2049 2041 "quote",
2050 2042 "syn 2.0.119",
@@ -3160,7 +3152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3160 3152 checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
3161 3153 dependencies = [
3162 3154 "fastrand 2.5.0",
3163 − "getrandom 0.3.4",
3155 + "getrandom 0.4.3",
3164 3156 "once_cell",
3165 3157 "rustix 1.1.5",
3166 3158 "windows-sys 0.61.2",
@@ -3394,6 +3386,12 @@ dependencies = [
3394 3386 "rustc-hash 2.1.3",
3395 3387 ]
3396 3388
3389 +[[package]]
3390 +name = "typed-path"
3391 +version = "0.12.4"
3392 +source = "registry+https://github.com/rust-lang/crates.io-index"
3393 +checksum = "54ec2755506471f54baf5e4bb5499d8e510cc1caf271270e778a736bd2d7336c"
3394 +
3397 3395 [[package]]
3398 3396 name = "typenum"
3399 3397 version = "1.20.1"
@@ -3853,7 +3851,7 @@ version = "0.1.11"
3853 3851 source = "registry+https://github.com/rust-lang/crates.io-index"
3854 3852 checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
3855 3853 dependencies = [
3856 − "windows-sys 0.48.0",
3854 + "windows-sys 0.61.2",
3857 3855 ]
3858 3856
3859 3857 [[package]]
@@ -4421,7 +4419,7 @@ dependencies = [
4421 4419 "futures-sink",
4422 4420 "futures-util",
4423 4421 "hex",
4424 − "nix",
4422 + "nix 0.26.4",
4425 4423 "once_cell",
4426 4424 "ordered-stream",
4427 4425 "rand",
@@ -4645,18 +4643,16 @@ dependencies = [
4645 4643
4646 4644 [[package]]
4647 4645 name = "zip"
4648 −version = "2.4.2"
4646 +version = "9.0.5"
4649 4647 source = "registry+https://github.com/rust-lang/crates.io-index"
4650 −checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50"
4648 +checksum = "6e04976da629d3b04204ba8105b396a193af8ad2583b2c79bfb3ab8af580a11e"
4651 4649 dependencies = [
4652 − "arbitrary",
4653 4650 "crc32fast",
4654 − "crossbeam-utils",
4655 − "displaydoc",
4656 4651 "flate2",
4657 4652 "indexmap",
4658 4653 "memchr",
4659 − "thiserror 2.0.20",
4654 + "nix 0.31.3",
4655 + "typed-path",
4660 4656 "zopfli",
4661 4657 ]
4662 4658
modified Cargo.toml
+2 −2
@@ -62,12 +62,12 @@ serde = { version = "1", features = ["derive"] }
62 62 toml = "0.8"
63 63 # Bundle export. No compression backends beyond deflate: a PNG is already
64 64 # deflated, so zstd/bzip2 would add dependencies to save nothing.
65 −zip = { version = "2", default-features = false, features = ["deflate"] }
65 +zip = { version = "9", default-features = false, features = ["deflate"] }
66 66 rfd = { version = "0.12", default-features = false, features = ["xdg-portal"] }
67 67
68 68 [profile.release]
69 69 opt-level = 2
70 70
71 71 [dev-dependencies]
72 −zip = { version = "2", default-features = false, features = ["deflate"] }
72 +zip = { version = "9", default-features = false, features = ["deflate"] }
73 73 image = { version = "0.25", default-features = false, features = ["png"] }
modified src/export.rs
+2 −2
@@ -103,8 +103,8 @@ pub fn export_zip(
103 103 ) -> Result<Vec<String>, String> {
104 104 let file = std::fs::File::create(path).map_err(|e| format!("{}: {e}", path.display()))?;
105 105 let mut zip = zip::ZipWriter::new(file);
106 − let options: zip::write::FileOptions<()> =
107 − zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Deflated);
106 + let options = zip::write::SimpleFileOptions::default()
107 + .compression_method(zip::CompressionMethod::Deflated);
108 108
109 109 let mut names = Vec::new();
110 110
modified tests/targets.rs
+56 −1
@@ -181,7 +181,7 @@ fn the_zip_bundle_carries_the_images_and_the_document() {
181 181 let f = fs::File::open(&out).unwrap();
182 182 let mut zip = zip::ZipArchive::new(f).expect("valid zip");
183 183 let entries: Vec<String> = (0..zip.len())
184 − .map(|i| zip.by_index(i).unwrap().name().to_string())
184 + .map(|i| zip.by_index(i).unwrap().name().unwrap().into_owned())
185 185 .collect();
186 186
187 187 assert!(entries.contains(&"mipmap-xxxhdpi/ic_icon.png".to_string()));
@@ -204,3 +204,58 @@ fn the_zip_bundle_carries_the_images_and_the_document() {
204 204 // layers use.
205 205 assert_eq!(decoded.width(), 192);
206 206 }
207 +
208 +#[test]
209 +fn the_zip_bundle_reads_back_as_it_was_written() {
210 + let t = Tmp::new("zip-roundtrip");
211 + let out = t.path().join("bundle.zip");
212 + let document = "version = 1\n# a comment that must survive the trip\n";
213 +
214 + let names = icon_studio::export::export_zip(
215 + &IconSpec::default(),
216 + &out,
217 + "ic_icon",
218 + Some(("ic_icon.toml", document)),
219 + )
220 + .expect("bundle written");
221 +
222 + let f = fs::File::open(&out).unwrap();
223 + let mut zip = zip::ZipArchive::new(f).expect("valid zip");
224 +
225 + // Exactly what export_zip says it wrote, in that order: nothing
226 + // dropped, nothing extra.
227 + let entries: Vec<String> = (0..zip.len())
228 + .map(|i| zip.by_index(i).unwrap().name().unwrap().into_owned())
229 + .collect();
230 + assert_eq!(entries, names);
231 +
232 + let read = |zip: &mut zip::ZipArchive<fs::File>, name: &str| {
233 + let mut entry = zip.by_name(name).unwrap();
234 + assert_eq!(
235 + entry.compression(),
236 + zip::CompressionMethod::Deflated,
237 + "{name} should be deflated"
238 + );
239 + let mut bytes = Vec::new();
240 + std::io::Read::read_to_end(&mut entry, &mut bytes).unwrap();
241 + bytes
242 + };
243 +
244 + // The text entries come back byte for byte.
245 + assert_eq!(read(&mut zip, "ic_icon.toml"), document.as_bytes());
246 + assert_eq!(
247 + read(&mut zip, "mipmap-anydpi-v26/ic_icon.xml"),
248 + icon_studio::export::adaptive_icon_xml("ic_icon").as_bytes()
249 + );
250 +
251 + // And every image is one the renderer would have produced: the
252 + // archive is a container, not a re-encoding.
253 + let spec = IconSpec::default();
254 + for (bucket, size) in icon_studio::export::DENSITIES {
255 + let fg = read(&mut zip, &format!("mipmap-{bucket}/ic_icon_foreground.png"));
256 + let expected = icon_studio::render::render_foreground(&spec, *size)
257 + .encode_png()
258 + .unwrap();
259 + assert_eq!(fg, expected, "{bucket} foreground");
260 + }
261 +}

Parents: a6e0e8a