diff --git a/crates/engine/src/cmd/fileio/screens.rs b/crates/engine/src/cmd/fileio/screens.rs index a1d2ae90c..b83024803 100644 --- a/crates/engine/src/cmd/fileio/screens.rs +++ b/crates/engine/src/cmd/fileio/screens.rs @@ -818,8 +818,11 @@ fn encode_and_write(export: ScreenExport, writer: &mut dyn FnMut(&str, &[u8]) -> files += 1; } } - // A cancel that came while the last file was encoded returns no files. - vectorcraft_doc::cancel::check()?; + // A cancel that came while the last file was encoded returns no files. A folder export + // without a ZIP has written every file by now, so it returns them. + if o.zip || folder.is_none() { + vectorcraft_doc::cancel::check()?; + } let names: Vec<&str> = out.kept.iter().map(|(n, _)| n.as_str()).collect(); if o.zip { let zip = super::zip::deflate(&out.kept, super::zip::FIXED_TIME, vectorcraft_doc::cancel::is_cancelled); diff --git a/crates/engine/src/cmd/fileio/tests_screens.rs b/crates/engine/src/cmd/fileio/tests_screens.rs index 78c8a147b..5da4bdf5e 100644 --- a/crates/engine/src/cmd/fileio/tests_screens.rs +++ b/crates/engine/src/cmd/fileio/tests_screens.rs @@ -727,6 +727,37 @@ fn a_cancel_after_the_last_file_returns_nothing() { let _ = std::fs::remove_dir_all(dir); } +/// A folder export without a ZIP writes each file as it goes: a cancel during the last step, a +/// row that repeats the one before it and writes nothing, comes after every file was written, so +/// the export returns the written files instead of the cancelled error. +#[cfg(not(target_arch = "wasm32"))] +#[test] +fn a_cancel_after_the_last_file_keeps_a_finished_folder_export() { + let mut s = session(1); + let dir = std::env::temp_dir().join(format!("vc-screens-cancel-folder-{}", std::process::id())); + let folder = dir.to_string_lossy().replace(std::path::MAIN_SEPARATOR, "/"); + let p = json!({ "formats": [{"format": "svg"}, {"format": "svg"}], "folder": folder }); + let prepared = s.prepare_screen_export("document.exportForScreens", &p).unwrap(); + let token = vectorcraft_doc::cancel::CancelToken::new(); + let on_last = token.clone(); + let sink: vectorcraft_doc::cancel::ProgressSink = std::rc::Rc::new(move |done, total, _| { + if done + 1 == total { + on_last.cancel(); + } + }); + let mut written = vec![]; + let r = vectorcraft_doc::cancel::scope(&token, Some(sink), || { + prepared.write(&mut |path: &str, _: &[u8]| { + written.push(path.to_string()); + Ok(()) + }) + }); + let r = r.unwrap(); + assert_eq!(written.len(), 1, "{written:?}"); + assert_eq!(r["files"], json!(written)); + let _ = std::fs::remove_dir_all(dir); +} + #[test] fn zip_exports_deflate_their_files() { let mut s = session(1);