mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Libraries: a User Defined library file whose reading panics is skipped
The desktop app reads the User Defined swatch and graphic style libraries while it creates its window: set_user_dir runs Libraries::rescan, which read and parsed every file in the folder outside the panic guard that commands have. A parser bug that panicked on one file stopped the app before its window appeared, at every launch while the file stayed in the folder. rescan now reads and parses each file inside guard::catch_panic and skips a file whose reading panics, as it skips one that can't be read. The other libraries are listed.
This commit is contained in:
@@ -152,13 +152,16 @@ impl<L: LibraryFile> Libraries<L> {
|
||||
}
|
||||
|
||||
/// Re-read the library files of the user library folder (unreadable ones are skipped). The
|
||||
/// folder is the app's own: automation roots don't apply to it ([`crate::file_access`]).
|
||||
/// folder is the app's own: automation roots don't apply to it ([`crate::file_access`]). Each
|
||||
/// file is read inside [`crate::guard::catch_panic`], and a file whose reading panics is
|
||||
/// skipped too.
|
||||
pub fn rescan(&mut self) {
|
||||
self.extra.retain(|e| e.info.category != "user");
|
||||
let Some(dir) = self.user_dir.clone() else { return };
|
||||
for path in library_files(&dir, L::EXTS) {
|
||||
let file = file_name(&path);
|
||||
let Some(lib) = crate::file_access::unconfined(|| read_file(&path)).ok().and_then(|b| L::read(&b, stem(&file)).ok()) else { continue };
|
||||
let read = || crate::file_access::unconfined(|| read_file(&path)).ok().and_then(|b| L::read(&b, stem(&file)).ok());
|
||||
let Some(lib) = crate::guard::catch_panic(read).ok().flatten() else { continue };
|
||||
let info = LibraryInfo { id: format!("user/{file}"), name: lib.name().to_string(), category: "user" };
|
||||
self.extra.push(Extra { info, path: Some(path), lib: Arc::new(lib) });
|
||||
}
|
||||
|
||||
@@ -189,6 +189,38 @@ fn libraries_saved_in_the_user_folder_are_user_defined() {
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
/// A library kind whose reader panics on a file that holds `trap`, as a parser bug would.
|
||||
#[derive(Default)]
|
||||
struct Trap(String);
|
||||
|
||||
impl cmd::swatchlib::LibraryFile for Trap {
|
||||
const EXTS: &'static [&'static str] = &["trap"];
|
||||
fn read(bytes: &[u8], stem: &str) -> std::result::Result<Self, String> {
|
||||
if bytes == b"trap" {
|
||||
panic!("a parser bug");
|
||||
}
|
||||
Ok(Self(stem.to_string()))
|
||||
}
|
||||
fn name(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// When reading one file of the user library folder panics, `set_user_dir` (as the desktop app
|
||||
/// calls it at launch) skips that file and lists the other one.
|
||||
#[test]
|
||||
fn a_user_library_file_whose_reading_panics_is_skipped() {
|
||||
let dir = temp_dir("user-panic");
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
std::fs::write(dir.join("Calm.trap"), "calm").unwrap();
|
||||
std::fs::write(dir.join("Loud.trap"), "trap").unwrap();
|
||||
let mut libraries = cmd::swatchlib::Libraries::<Trap>::default();
|
||||
libraries.set_user_dir(Some(dir.to_string_lossy().to_string()));
|
||||
let names: Vec<&str> = libraries.infos().map(|l| l.name.as_str()).collect();
|
||||
assert_eq!(names, ["Calm"]);
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn another_documents_swatches_load_as_a_library() {
|
||||
let mut s = session();
|
||||
|
||||
Reference in New Issue
Block a user