Address trash moves and deletes by path, not fileid

Trashing or emptying the trash failed on every scanned image with
"operation unsupported by this backend: fetch by fileid requires a
path".

RemoteId::Stable(oc:fileid) is an identity — it answers whether a file
is the same one after a move, and keys the thumbnail shards. It is not
an address: WebDAV exposes no fileid-addressable endpoint, so the
Nextcloud backend serves get/delete/move_to by path and rejects a bare
Stable. Both trash workers preferred the fileid whenever the catalog
knew one, so the unreachable Path fallback was the only arm that would
have worked, and the failure hit every properly-scanned image rather
than some edge case.

Address by path at both sites, and keep the fileid for what it is for:
the identity MOVE preserves, and the key the thumbnail cleanup uses.

Move::file_id was documented as "the id the MOVE addresses", which is
the wrong claim that seeded this; corrected, along with a note on
RemoteId itself so the distinction is stated where the type is defined.

Only the backend rejection was covered by a test. Added the positive
case, since that contract is what the call sites now depend on. The
workers build their own NextcloudBackend, so no test can reach the
call sites directly — closing that would mean injecting the backend,
which is left alone here.

Verified by build and test; not exercised against a live server.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-09 21:39:35 +02:00
co-authored by Claude Opus 5
parent 9a24623e35
commit b4c1645c7a
3 changed files with 52 additions and 14 deletions
+10
View File
@@ -66,6 +66,16 @@ impl std::fmt::Display for RemotePath {
/// survives server-side rename and move, so a move is detected as a move
/// rather than a delete plus a re-download of a large file (FR-NC-5).
/// Otherwise it falls back to the path, and moves cost a transfer.
///
/// **Identity is not an address.** `Stable` answers "is this the same object
/// as before?"; it does not necessarily name one the backend can address.
/// Nextcloud, the only backend, exposes no fileid-addressable endpoint, so it
/// serves [`RemoteBackend::get`], [`delete`](RemoteBackend::delete) and
/// [`move_to`](RemoteBackend::move_to) by path alone and rejects a `Stable`
/// with [`RemoteError::Unsupported`]. Callers holding both — the catalog keeps
/// the fileid beside `remote_path` — must pass the path to those three and
/// keep the fileid for what it is good for: matching moves across scans, and
/// keying the thumbnail shards.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum RemoteId {
/// A server-assigned identifier, e.g. Nextcloud's `oc:fileid`.