Let the interface hold a backend without knowing whose it is
`dr-sync` defines `RemoteBackend` and a capability model the engine adapts to, so a second backend can be added without touching the code that uses one. That boundary was documentation. Seven files in `dr-ui` constructed a `NextcloudBackend` directly, ten functions took one by concrete type, and exactly two call sites in the tree — both inside `dr-sync` itself — ever held the trait object. A WebDAV or local-folder backend would have had a well-written trait to implement and nowhere to go afterwards. The change is smaller than the finding suggests, because the trait was already right. Every method the UI has ever called on a backend — `get`, `put`, `list`, `delete`, `create_dir`, `move_to` — was already on it, so nothing had to be added and no behaviour moved. Ten signatures widened to `&dyn RemoteBackend`, sixteen constructions became `remote::connect`, and `remote.rs` is now the only file in the interface that names a connector. `connect` returns `Result<Box<dyn RemoteBackend>, RemoteError>`. The error type is `dr-sync`'s rather than the connector's, which is why every call site kept its shape — the `match`, the `let Ok(..) else`, and `.map_err(ScanFailure::local)?` all still read as they did. One wrinkle worth recording: `&Box<dyn Trait>` does not reach `&dyn Trait` on its own. The compiler reaches for unsizing, which wants `Box<dyn RemoteBackend>: RemoteBackend`, and reports a confusing missing impl rather than suggesting a deref. Twelve call sites therefore say `&*backend`, and two say `let backend: &dyn RemoteBackend = &*backend` where a borrow is shared across lanes. What this does *not* do is abstract credentials. `AppCredentials` is an app password from Login Flow v2 — a Nextcloud protocol, not a general notion of authenticating to a remote — and seven files still name it. An OAuth token, a bucket key pair and an app password have no useful common shape, so deciding what an account is across backends before a second one exists would be a confident guess. code-health.md CH-2 now records that as the remaining half, and it should wait for the backend that forces it. Verified: fmt clean, clippy clean at -D warnings, 2041 tests pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,7 +35,7 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use dr_sync::{RemoteBackend, RemoteId, RemotePath};
|
||||
use dr_sync_nextcloud::{AppCredentials, NextcloudBackend};
|
||||
use dr_sync_nextcloud::AppCredentials;
|
||||
use dr_thumbs::ThumbStore;
|
||||
|
||||
/// Folder under the library root holding derived state.
|
||||
@@ -117,7 +117,7 @@ pub fn spawn_sync(
|
||||
};
|
||||
|
||||
rt.block_on(async {
|
||||
let backend = match NextcloudBackend::new(&creds, &user_id) {
|
||||
let backend = match crate::remote::connect(&creds, &user_id) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
let _ = tx.send(SyncMessage::Failed(e.to_string()));
|
||||
@@ -125,7 +125,7 @@ pub fn spawn_sync(
|
||||
}
|
||||
};
|
||||
|
||||
match run(&backend, &root, &thumbs_dir, &catalog_path, &scratch, &tx).await {
|
||||
match run(&*backend, &root, &thumbs_dir, &catalog_path, &scratch, &tx).await {
|
||||
Ok(report) => {
|
||||
let _ = tx.send(SyncMessage::Finished(Box::new(report)));
|
||||
}
|
||||
@@ -140,7 +140,7 @@ pub fn spawn_sync(
|
||||
}
|
||||
|
||||
async fn run(
|
||||
backend: &NextcloudBackend,
|
||||
backend: &dyn RemoteBackend,
|
||||
root: &str,
|
||||
thumbs_dir: &Path,
|
||||
catalog_path: &Path,
|
||||
@@ -193,7 +193,7 @@ fn derived_path(root: &str) -> RemotePath {
|
||||
/// namespace, and [`ThumbStore::adopted`] then tracks what has been merged by
|
||||
/// remote name instead of by our own ids.
|
||||
async fn sync_shards(
|
||||
backend: &NextcloudBackend,
|
||||
backend: &dyn RemoteBackend,
|
||||
base: &RemotePath,
|
||||
thumbs_dir: &Path,
|
||||
scratch: &Path,
|
||||
@@ -323,7 +323,7 @@ async fn sync_shards(
|
||||
/// carrier. So this exports the catalog's new faces into the local shard store
|
||||
/// first, syncs the shards, and imports whatever arrived back into the catalog.
|
||||
async fn sync_face_shards(
|
||||
backend: &NextcloudBackend,
|
||||
backend: &dyn RemoteBackend,
|
||||
base: &RemotePath,
|
||||
catalog_path: &Path,
|
||||
scratch: &Path,
|
||||
@@ -482,7 +482,7 @@ fn legacy_upload_of_ours(store: &ThumbStore, id: u32, remote_size: u64) -> bool
|
||||
/// describes local state (folder ETags, cache paths, job rows) and importing
|
||||
/// another device's version would be actively wrong.
|
||||
async fn sync_catalog(
|
||||
backend: &NextcloudBackend,
|
||||
backend: &dyn RemoteBackend,
|
||||
base: &RemotePath,
|
||||
catalog_path: &Path,
|
||||
scratch: &Path,
|
||||
|
||||
Reference in New Issue
Block a user