Merge pull request #3331 from dotCooCoo/fix/user-library-file-panic-is-skipped

Libraries: a User Defined library file whose reading panics is skipped
This commit is contained in:
BFlat
2026-10-21 15:40:35 +07:00
committed by GitHub
2 changed files with 37 additions and 2 deletions
+5 -2
View File
@@ -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) });
}
+32
View File
@@ -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();