Package: a file the writer refuses is named once in the error

File › Package… put the file's path in front of every error its writer
returned. A refusal from the automation write root already names the
path, so the dialog's error and the status bar named the file twice,
for example on Windows for a document named aux:
`<path>: automation path rejected: reserved device names are not
allowed: <path>`.

The dialog now keeps a writer error that already contains the path as
it is, and puts the path in front of one that does not, such as the
desktop's "Access is denied. (os error 5)".
This commit is contained in:
dotCooCoo
2026-10-22 23:36:57 +07:00
committed by bflatastic
parent 6e091a33f5
commit 4a4c8c9d49
2 changed files with 31 additions and 1 deletions
+3 -1
View File
@@ -129,7 +129,9 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
let later = Rc::new(Cell::new(false));
let in_background = later.clone();
let work = move |write: Writer| {
let mut write = |path: &str, bytes: &[u8]| write(path, bytes).map_err(|e| EngineError::Other(format!("{path}: {e}")));
// A writer error that already names the file (an automation root's refusal) is kept as it is.
let mut write =
|path: &str, bytes: &[u8]| write(path, bytes).map_err(|e| EngineError::Other(if e.contains(path) { e } else { format!("{path}: {e}") }));
package.write(&mut write).map_err(|e| e.to_string())
};
let r = background::run(app, enabled, label, None, work, move |app, r| packaged(app, r, web, in_background.get()))?;
@@ -6,6 +6,7 @@ use std::sync::Arc;
use serde_json::json;
use vectorcraft_engine::Session;
use vectorcraft_engine::file_access::{Access, AutomationRoots};
use super::*;
use crate::background;
@@ -298,6 +299,33 @@ fn package_status_without_notes_has_no_extra_spaces() {
assert!(!detail.contains(" ."), "{detail}");
}
/// A file the writer refuses is named once in the status bar: a refusal from the automation-root
/// check already names the path, and an error without it gets the path in front.
#[test]
fn a_refused_file_is_named_once() {
let dir = Folder::new("refused");
let mut app = saved(&dir);
app.session.execute("prefs.set", &json!({"values": {"backgroundExport": false}})).unwrap();
let (out, elsewhere, other) = (dir.path("out"), dir.path("elsewhere"), dir.path("other"));
std::fs::create_dir_all(&elsewhere).unwrap();
std::fs::create_dir_all(&other).unwrap();
let doc = std::path::Path::new(&out).join("card Folder").join("card.vectorcraft").to_string_lossy().into_owned();
let (elsewhere, other) = (std::path::Path::new(&elsewhere), std::path::Path::new(&other));
for write_roots in [vec![elsewhere], vec![elsewhere, other]] {
let roots = AutomationRoots::new(&[], &write_roots).unwrap().unwrap();
let refused = roots.check(&doc, Access::Write).unwrap_err();
assert!(refused.ends_with(&doc), "{refused}");
app.services.write = Some(Box::new(move |p: &str, _: &[u8]| roots.check(p, Access::Write)));
package_dialog(&mut app, &out);
assert_eq!(confirm(&mut app), Err(refused.clone()));
assert_eq!(app.ui.status, refused);
}
app.services.write = Some(Box::new(|_: &str, _: &[u8]| Err("Access is denied. (os error 5)".into())));
package_dialog(&mut app, &out);
assert!(confirm(&mut app).is_err());
assert_eq!(app.ui.status, format!("{doc}: Access is denied. (os error 5)"));
}
#[test]
fn package_without_a_location_says_so() {
let dir = Folder::new("empty");