From eb2e1ed9e4cd78e1f2f884dd0fe835fc871c8a91 Mon Sep 17 00:00:00 2001 From: nardor Date: Sun, 17 Nov 2024 11:53:23 +0100 Subject: [PATCH 1/2] feat(backend): add is_async_compatible method to ReadBackend and Repository --- crates/backend/src/local.rs | 7 +++++++ crates/backend/src/opendal.rs | 9 +++++++++ crates/backend/src/rclone.rs | 5 +++++ crates/backend/src/rest.rs | 10 ++++++++++ crates/core/src/backend.rs | 21 ++++++++++++++++++++- crates/core/src/backend/cache.rs | 4 ++++ crates/core/src/backend/decrypt.rs | 8 ++++++-- crates/core/src/backend/dry_run.rs | 4 ++++ crates/core/src/backend/hotcold.rs | 4 ++++ crates/core/src/backend/warm_up.rs | 4 ++++ crates/core/src/repository.rs | 15 ++++++++++++++- crates/testing/src/backend.rs | 5 +++++ 12 files changed, 92 insertions(+), 4 deletions(-) diff --git a/crates/backend/src/local.rs b/crates/backend/src/local.rs index 02cf6c5d..e43c4c81 100644 --- a/crates/backend/src/local.rs +++ b/crates/backend/src/local.rs @@ -398,6 +398,13 @@ impl ReadBackend for LocalBackend { Ok(vec.into()) } + + /// [`LocalBackend`] doesn't use `async`, even under the hood. + /// + /// So it can be called from `async` features. + fn is_async_compatible(&self) -> bool { + true + } } impl WriteBackend for LocalBackend { diff --git a/crates/backend/src/opendal.rs b/crates/backend/src/opendal.rs index 0165d0e4..9e09a2ad 100644 --- a/crates/backend/src/opendal.rs +++ b/crates/backend/src/opendal.rs @@ -386,6 +386,15 @@ impl ReadBackend for OpenDALBackend { )? .to_bytes()) } + + /// [`OpenDALBackend`] is `sync` and uses `block_on(async Fn)` under the hood. + /// + /// When implementing `rustic_core` using this backend in some `async` features will not work. + /// + /// see + fn is_async_compatible(&self) -> bool { + false + } } impl WriteBackend for OpenDALBackend { diff --git a/crates/backend/src/rclone.rs b/crates/backend/src/rclone.rs index 94bb4b3b..c8522900 100644 --- a/crates/backend/src/rclone.rs +++ b/crates/backend/src/rclone.rs @@ -353,6 +353,11 @@ impl ReadBackend for RcloneBackend { ) -> RusticResult { self.rest.read_partial(tpe, id, cacheable, offset, length) } + + /// [`RcloneBackend`] uses [`RestBackend`]. + fn is_async_compatible(&self) -> bool { + self.rest.is_async_compatible() + } } impl WriteBackend for RcloneBackend { diff --git a/crates/backend/src/rest.rs b/crates/backend/src/rest.rs index 2293afa6..b4f07a23 100644 --- a/crates/backend/src/rest.rs +++ b/crates/backend/src/rest.rs @@ -413,6 +413,16 @@ impl ReadBackend for RestBackend { ) .map_err(construct_backoff_error) } + + /// [`RestBackend`] uses `reqwest` which blocking implementation + /// uses an `async` runtime under the hood. + /// + /// When implementing `rustic_core` using this backend in some `async` features will not work. + /// + /// see + fn is_async_compatible(&self) -> bool { + false + } } fn construct_backoff_error(err: backoff::Error) -> Box { diff --git a/crates/core/src/backend.rs b/crates/core/src/backend.rs index 2179671b..79a54d40 100644 --- a/crates/core/src/backend.rs +++ b/crates/core/src/backend.rs @@ -174,6 +174,21 @@ pub trait ReadBackend: Send + Sync + 'static { fn warm_up(&self, _tpe: FileType, _id: &Id) -> RusticResult<()> { Ok(()) } + + /// Getter to now if some backend is compatible + /// with async features in `rustic_core` implementations. + /// + /// ## Default impl + /// + /// A default impl allow these change to be non-breaking. + /// By default it is `false`, async compatibility is opt-in. + /// + /// ## Temporary + /// + /// see + fn is_async_compatible(&self) -> bool { + false + } } /// Trait for Searching in a backend. @@ -343,7 +358,7 @@ pub trait WriteBackend: ReadBackend { mock! { Backend {} - impl ReadBackend for Backend{ + impl ReadBackend for Backend { fn location(&self) -> String; fn list_with_size(&self, tpe: FileType) -> RusticResult>; fn read_full(&self, tpe: FileType, id: &Id) -> RusticResult; @@ -355,6 +370,7 @@ mock! { offset: u32, length: u32, ) -> RusticResult; + fn is_async_compatible(&self) -> bool; } impl WriteBackend for Backend { @@ -400,6 +416,9 @@ impl ReadBackend for Arc { self.deref() .read_partial(tpe, id, cacheable, offset, length) } + fn is_async_compatible(&self) -> bool { + self.deref().is_async_compatible() + } } impl std::fmt::Debug for dyn WriteBackend { diff --git a/crates/core/src/backend/cache.rs b/crates/core/src/backend/cache.rs index 8d82ecb0..9ac8c5d5 100644 --- a/crates/core/src/backend/cache.rs +++ b/crates/core/src/backend/cache.rs @@ -162,6 +162,10 @@ impl ReadBackend for CachedBackend { fn warm_up(&self, tpe: FileType, id: &Id) -> RusticResult<()> { self.be.warm_up(tpe, id) } + + fn is_async_compatible(&self) -> bool { + self.be.is_async_compatible() + } } impl WriteBackend for CachedBackend { diff --git a/crates/core/src/backend/decrypt.rs b/crates/core/src/backend/decrypt.rs index 414084f4..fa1cc7c6 100644 --- a/crates/core/src/backend/decrypt.rs +++ b/crates/core/src/backend/decrypt.rs @@ -216,7 +216,7 @@ pub trait DecryptWriteBackend: WriteBackend + Clone + 'static { /// /// # Returns /// - /// The processed data, the original data length and when compression is used, the uncomressed length + /// The processed data, the original data length and when compression is used, the uncompressed length fn process_data(&self, data: &[u8]) -> RusticResult<(Vec, u32, Option)>; /// Writes the given data to the backend without compression and returns the id of the data. @@ -559,7 +559,7 @@ impl DecryptWriteBackend for DecryptBackend { /// /// # Arguments /// - /// * `extra_echeck` - The compression level to use for zstd. + /// * `extra_verify` - The compression level to use for zstd. fn set_extra_verify(&mut self, extra_verify: bool) { self.extra_verify = extra_verify; } @@ -622,6 +622,10 @@ impl ReadBackend for DecryptBackend { ) -> RusticResult { self.be.read_partial(tpe, id, cacheable, offset, length) } + + fn is_async_compatible(&self) -> bool { + self.be.is_async_compatible() + } } impl WriteBackend for DecryptBackend { diff --git a/crates/core/src/backend/dry_run.rs b/crates/core/src/backend/dry_run.rs index 5bd07cb8..300656fe 100644 --- a/crates/core/src/backend/dry_run.rs +++ b/crates/core/src/backend/dry_run.rs @@ -103,6 +103,10 @@ impl ReadBackend for DryRunBackend { ) -> RusticResult { self.be.read_partial(tpe, id, cacheable, offset, length) } + + fn is_async_compatible(&self) -> bool { + self.be.is_async_compatible() + } } impl DecryptWriteBackend for DryRunBackend { diff --git a/crates/core/src/backend/hotcold.rs b/crates/core/src/backend/hotcold.rs index e5306e2f..594e1851 100644 --- a/crates/core/src/backend/hotcold.rs +++ b/crates/core/src/backend/hotcold.rs @@ -75,6 +75,10 @@ impl ReadBackend for HotColdBackend { fn warm_up(&self, tpe: FileType, id: &Id) -> RusticResult<()> { self.be.warm_up(tpe, id) } + + fn is_async_compatible(&self) -> bool { + self.be.is_async_compatible() + } } impl WriteBackend for HotColdBackend { diff --git a/crates/core/src/backend/warm_up.rs b/crates/core/src/backend/warm_up.rs index 3e25d49a..8406176d 100644 --- a/crates/core/src/backend/warm_up.rs +++ b/crates/core/src/backend/warm_up.rs @@ -59,6 +59,10 @@ impl ReadBackend for WarmUpAccessBackend { _ = self.be.read_partial(tpe, id, false, 0, 1); Ok(()) } + + fn is_async_compatible(&self) -> bool { + self.be.is_async_compatible() + } } impl WriteBackend for WarmUpAccessBackend { diff --git a/crates/core/src/repository.rs b/crates/core/src/repository.rs index a94f108b..ef474e2a 100644 --- a/crates/core/src/repository.rs +++ b/crates/core/src/repository.rs @@ -686,6 +686,19 @@ impl Repository { pub fn list(&self) -> RusticResult> { Ok(self.be.list(T::TYPE)?.into_iter().map(Into::into)) } + + /// Check if one of this repository backend is incompatible + /// with async features in `rustic_core` implementations. + /// + /// see + pub fn is_async_compatible(&self) -> bool { + // check if be or be_hot is incompatible with async + self.be.is_async_compatible() + && self + .be_hot + .as_ref() + .map_or(true, ReadBackend::is_async_compatible) + } } impl Repository { @@ -1874,7 +1887,7 @@ impl Repository { impl Repository { /// Run a backup of `source` using the given options. /// - /// You have to give a preflled [`SnapshotFile`] which is modified and saved. + /// You have to give a prefilled [`SnapshotFile`] which is modified and saved. /// /// # Arguments /// diff --git a/crates/testing/src/backend.rs b/crates/testing/src/backend.rs index 418bd320..64e9b070 100644 --- a/crates/testing/src/backend.rs +++ b/crates/testing/src/backend.rs @@ -58,6 +58,11 @@ pub mod in_memory_backend { ) -> RusticResult { Ok(self.0.read().unwrap()[tpe][id].slice(offset as usize..(offset + length) as usize)) } + + /// [`InMemoryBackend`] doesn't use `async`, even under the hood. + fn is_async_compatible(&self) -> bool { + true + } } impl WriteBackend for InMemoryBackend { From c968a76740e7a289989cb30339d3d19124589fbb Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:50:23 +0100 Subject: [PATCH 2/2] Update crates/core/src/backend.rs --- crates/core/src/backend.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core/src/backend.rs b/crates/core/src/backend.rs index 79a54d40..25896f89 100644 --- a/crates/core/src/backend.rs +++ b/crates/core/src/backend.rs @@ -175,7 +175,7 @@ pub trait ReadBackend: Send + Sync + 'static { Ok(()) } - /// Getter to now if some backend is compatible + /// Getter to determine if some backend is compatible /// with async features in `rustic_core` implementations. /// /// ## Default impl