Merge: recover a damaged catalog, and capture a crash locally
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -862,7 +862,13 @@ pub fn open(
|
||||
|
||||
// Before the scan, not after it: the grid can be filled from disk now and
|
||||
// the scan is only ever going to add to it.
|
||||
show_catalog_now(window, &ctl, &path, &coll_ctl);
|
||||
//
|
||||
// And it is the gate on the scan, not merely a prelude to it — a damaged
|
||||
// catalog has a question on screen, and a scan writing into it while that
|
||||
// question is unanswered is how the last good copy gets destroyed.
|
||||
if !show_catalog_now(window, &ctl, &path, &coll_ctl) {
|
||||
return;
|
||||
}
|
||||
|
||||
let rx = library::spawn_scan(
|
||||
conn.clone(),
|
||||
@@ -1095,7 +1101,7 @@ fn drain_scan(
|
||||
/// the same operation: a scan is the only request that both proves the server
|
||||
/// is reachable and brings the catalog up to date. Keeping them one function
|
||||
/// is what stops "retry" from quietly becoming a weaker probe than "rescan".
|
||||
fn start_rescan(
|
||||
pub(crate) fn start_rescan(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<LibraryController>,
|
||||
coll_ctl: &Rc<crate::collections_ui::CollectionsController>,
|
||||
@@ -1916,23 +1922,38 @@ fn schedule_reload(window: &AppWindow, ctl: &Rc<LibraryController>) {
|
||||
/// state already says "Scanning…", and an error here would contradict a scan
|
||||
/// that is working perfectly. `Catalog::open` creates the file in that case, so
|
||||
/// what the grid reads is an empty catalog rather than a failure.
|
||||
fn show_catalog_now(
|
||||
/// Returns whether it is safe to go on and scan.
|
||||
///
|
||||
/// `false` means the catalog is damaged and the recovery question is up. The
|
||||
/// caller must not start a scan on that answer: `Catalog::open` succeeds on a
|
||||
/// file whose header survived, so the scan would write ETags and image rows
|
||||
/// into damaged pages while the user is still reading the question — turning a
|
||||
/// file that had a backup into one where the backup is the only copy left.
|
||||
pub(crate) fn show_catalog_now(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<LibraryController>,
|
||||
catalog_path: &std::path::Path,
|
||||
coll_ctl: &Rc<crate::collections_ui::CollectionsController>,
|
||||
) {
|
||||
) -> bool {
|
||||
if ctl.catalog.borrow().is_some() {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
let cat = match Catalog::open(catalog_path) {
|
||||
// Verified rather than plain: this is the once-per-launch moment where a
|
||||
// full check is affordable and there is a user in front of it who can
|
||||
// answer the question. See `dr_catalog::recovery` for why it is not on
|
||||
// every open.
|
||||
let cat = match Catalog::open_verified(catalog_path) {
|
||||
Ok(cat) => cat,
|
||||
Err(dr_catalog::CatalogError::Corrupt { detail }) => {
|
||||
crate::recovery_ui::offer(window, catalog_path, &detail);
|
||||
return false;
|
||||
}
|
||||
Err(e) => {
|
||||
// Not surfaced: the scan is the thing that has to work, and it is
|
||||
// still running. If it fails too, it reports for both of them.
|
||||
log::info!("no catalog to show before the scan: {e}");
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1964,6 +1985,19 @@ fn show_catalog_now(
|
||||
crate::collections_ui::refresh_tree(window, coll_ctl, &cat);
|
||||
*ctl.catalog.borrow_mut() = Some(cat);
|
||||
load_window(window, ctl);
|
||||
true
|
||||
}
|
||||
|
||||
/// Drop the open catalog, so the next `show_catalog_now` opens the file
|
||||
/// again rather than returning early.
|
||||
///
|
||||
/// Only recovery needs this, and it needs it for a specific reason: the file
|
||||
/// under that connection has been replaced. A handle to the catalog that was
|
||||
/// there before is a handle to a file that no longer has a name, and every
|
||||
/// read through it would return the damaged pages the recovery just moved out
|
||||
/// of the way.
|
||||
pub(crate) fn forget_catalog(ctl: &Rc<LibraryController>) {
|
||||
*ctl.catalog.borrow_mut() = None;
|
||||
}
|
||||
|
||||
/// What one cell of the outgoing model is worth keeping.
|
||||
@@ -5644,6 +5678,11 @@ pub fn wire<F>(
|
||||
start_rescan(&w, &ctl, &coll_ctl);
|
||||
});
|
||||
}
|
||||
|
||||
// Last, and in its own module: the answers to a damaged catalog have
|
||||
// nothing to do with the library view except that they run before it
|
||||
// exists.
|
||||
crate::recovery_ui::wire(window, &ctl, &coll_ctl);
|
||||
}
|
||||
|
||||
/// Reload the grid after the filter changed.
|
||||
|
||||
Reference in New Issue
Block a user