Refuse a scan whose root has gone, instead of reporting it empty
FR-PLAT-AND-2, and a silent failure on both platforms. `dr_sync::scan` stepped over a NotFound or PermissionDenied the way it does for a child that vanished mid-walk -- correct for a child, wrong for the root, where it ended the walk, returned Ok with nothing in it, and reported a successful scan of a library that was no longer there. A lost root is now its own error. The images under it are marked Availability::Offline per FR-CAT-9 and no catalog row is deleted; `library::persist` clears the mark per file as each one is listed again, so a root that comes back needs no repair step. Partly satisfied rather than closed, and the gap is worth stating. The recovery half is real and reachable on Android today, because `map_status` turns Nextcloud's 403 and 404 into it and Nextcloud is how a phone actually gets a library in this build. The causes the requirement names -- revocation, reinstall, a removed card -- are properties of a persisted tree permission, and there is none: SAF does not exist here, `SourceRef::Document` is constructed only in test modules, and `LocalStorage` rejects the variant outright. When SAF lands it becomes a third producer of this error and nothing above it changes, which is why the discovery belongs in the connector. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -216,10 +216,17 @@ impl std::fmt::Debug for FolderBackend {
|
||||
impl FolderBackend {
|
||||
/// Open the folder at `root`.
|
||||
///
|
||||
/// The directory must exist now. It may stop existing later — a drive
|
||||
/// unplugged, a mount dropped — and that surfaces per-operation as
|
||||
/// [`RemoteError::Network`], which is what puts the app into offline mode
|
||||
/// and leaves the catalog readable, exactly as a dead server does.
|
||||
/// The directory must exist now, and not existing is
|
||||
/// [`RemoteError::RootUnavailable`] — the library folder could not be
|
||||
/// opened, which is the whole of what this knows. A drive unplugged
|
||||
/// between sessions and a path typed wrongly at setup are the same
|
||||
/// observation from here, and both are answered the same way: keep the
|
||||
/// catalog, say which folder, and offer it again (FR-PLAT-AND-2).
|
||||
///
|
||||
/// A mount dropped *during* a session surfaces per-operation as
|
||||
/// [`RemoteError::Network`] instead, which is what puts the app into
|
||||
/// offline mode and leaves the catalog readable, exactly as a dead server
|
||||
/// does.
|
||||
pub fn new(root: impl Into<PathBuf>) -> Result<Self, RemoteError> {
|
||||
Self::with_vfs(root, Arc::new(NoVfs))
|
||||
}
|
||||
@@ -232,7 +239,15 @@ impl FolderBackend {
|
||||
pub fn with_vfs(root: impl Into<PathBuf>, vfs: Arc<dyn Vfs>) -> Result<Self, RemoteError> {
|
||||
let root = root.into();
|
||||
if !root.is_dir() {
|
||||
return Err(RemoteError::Configuration(format!(
|
||||
// TRACES: FR-PLAT-AND-2
|
||||
// Not `Configuration`, which is where this lived while there was
|
||||
// nothing better. The distinction that matters is not "was the
|
||||
// account written wrongly" — which nothing here can know — but
|
||||
// "can this library be opened", and a caller that knows the
|
||||
// library was working yesterday can act on the second answer:
|
||||
// mark what it holds as offline rather than deleting it, and ask
|
||||
// for the folder again (FR-CAT-9).
|
||||
return Err(RemoteError::RootUnavailable(format!(
|
||||
"{} is not a folder",
|
||||
root.display()
|
||||
)));
|
||||
|
||||
Reference in New Issue
Block a user