neiam
/icon-studio
public · Issues · Pulls · Labels · Forks · Compare · Actions failure · Badges · Packages
⭐
Log in to mark this repository.
Move the bundle export to zip 9
30a56d2 · Gabriel Morell · 2026-10-23 05:38
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