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:
@@ -0,0 +1,40 @@
|
||||
// TRACES: FR-NC-12
|
||||
//! The one place the interface names a backend.
|
||||
//!
|
||||
//! `dr-sync` defines [`RemoteBackend`] and a capability model the engine adapts
|
||||
//! to, so that a second backend can be added without touching the code that
|
||||
//! uses one (ARCH §8.1). Until this module existed that boundary was
|
||||
//! documentation: seven files in `dr-ui` constructed a `NextcloudBackend`
|
||||
//! directly and ten functions took one by concrete type, so the abstraction
|
||||
//! bought nothing it was designed for and a WebDAV or local-folder backend
|
||||
//! would have had nowhere to go.
|
||||
//!
|
||||
//! Everything above this module now works through `&dyn RemoteBackend`. Adding
|
||||
//! a backend is implementing the trait and changing [`connect`] — not editing
|
||||
//! seven files.
|
||||
//!
|
||||
//! ## What is deliberately still Nextcloud-shaped
|
||||
//!
|
||||
//! Credentials. [`AppCredentials`] is an app password obtained through Login
|
||||
//! Flow v2, which is a Nextcloud protocol rather than a general notion of
|
||||
//! "how one authenticates to a remote". Abstracting it needs a decision about
|
||||
//! what an account *is* across backends — an OAuth token, a bucket key pair
|
||||
//! and an app password have no useful common shape — and inventing one before
|
||||
//! a second backend exists would produce a wrong answer confidently. That is
|
||||
//! the remaining half of this seam, and it is a design problem rather than a
|
||||
//! mechanical one.
|
||||
|
||||
use dr_sync::{RemoteBackend, RemoteError};
|
||||
use dr_sync_nextcloud::{AppCredentials, NextcloudBackend};
|
||||
|
||||
/// Open a connection to the configured remote.
|
||||
///
|
||||
/// Returns the trait object every caller should hold. The error type is
|
||||
/// `dr-sync`'s rather than the connector's, so a caller handles a failure
|
||||
/// without learning which backend produced it.
|
||||
pub(crate) fn connect(
|
||||
creds: &AppCredentials,
|
||||
user_id: &str,
|
||||
) -> Result<Box<dyn RemoteBackend>, RemoteError> {
|
||||
Ok(Box::new(NextcloudBackend::new(creds, user_id)?))
|
||||
}
|
||||
Reference in New Issue
Block a user