This is an automated email from the ASF dual-hosted git repository.

mneumann pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs-object-store.git


The following commit(s) were added to refs/heads/main by this push:
     new 0d5bc71  fix: wrappers and default trait methods (#537)
0d5bc71 is described below

commit 0d5bc71fb75525d98f52119125acaaa20c653a5c
Author: Marco Neumann <[email protected]>
AuthorDate: Mon Nov 10 12:04:19 2025 +0100

    fix: wrappers and default trait methods (#537)
    
    Document that wrapper SHOULD implement all trait methods. Introduce
    clippy lint for our builtin wrappers and fix `ChunkedStore`.
---
 src/chunked.rs  | 12 ++++++++++++
 src/lib.rs      | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 src/limit.rs    |  1 +
 src/prefix.rs   |  1 +
 src/throttle.rs |  1 +
 5 files changed, 68 insertions(+), 2 deletions(-)

diff --git a/src/chunked.rs b/src/chunked.rs
index 1775d95..cbd66f6 100644
--- a/src/chunked.rs
+++ b/src/chunked.rs
@@ -61,6 +61,7 @@ impl Display for ChunkedStore {
 }
 
 #[async_trait]
+#[deny(clippy::missing_trait_methods)]
 impl ObjectStore for ChunkedStore {
     async fn put_opts(
         &self,
@@ -138,6 +139,9 @@ impl ObjectStore for ChunkedStore {
         self.inner.get_range(location, range).await
     }
 
+    async fn get_ranges(&self, location: &Path, ranges: &[Range<u64>]) -> 
Result<Vec<Bytes>> {
+        self.inner.get_ranges(location, ranges).await
+    }
     async fn head(&self, location: &Path) -> Result<ObjectMeta> {
         self.inner.head(location).await
     }
@@ -173,9 +177,17 @@ impl ObjectStore for ChunkedStore {
         self.inner.copy(from, to).await
     }
 
+    async fn rename(&self, from: &Path, to: &Path) -> Result<()> {
+        self.inner.rename(from, to).await
+    }
+
     async fn copy_if_not_exists(&self, from: &Path, to: &Path) -> Result<()> {
         self.inner.copy_if_not_exists(from, to).await
     }
+
+    async fn rename_if_not_exists(&self, from: &Path, to: &Path) -> Result<()> 
{
+        self.inner.rename_if_not_exists(from, to).await
+    }
 }
 
 #[cfg(test)]
diff --git a/src/lib.rs b/src/lib.rs
index c3181d6..c4c39dc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -615,7 +615,7 @@ pub type MultipartId = String;
 
 /// Universal API to multiple object store services.
 ///
-/// For more convience methods, check [`ObjectStoreExt`].
+/// For more convenience methods, check [`ObjectStoreExt`].
 ///
 /// # Contract
 /// This trait is meant as a contract between object store implementations
@@ -624,7 +624,57 @@ pub type MultipartId = String;
 ///
 /// The [`ObjectStoreExt`] acts as an API/contract between `object_store`
 /// and the store users and provides additional methods that may be simpler to 
use but overlap
-/// in functionality with `ObjectStore`
+/// in functionality with [`ObjectStore`].
+///
+/// # Wrappers
+/// If you wrap an [`ObjectStore`] -- e.g. to add observability -- you SHOULD
+/// implement all trait methods. This ensures that defaults implementations
+/// that are overwritten by the wrapped store are also used by the wrapper.
+/// For example:
+///
+/// ```ignore
+/// struct MyStore {
+///     ...
+/// }
+///
+/// #[async_trait]
+/// impl ObjectStore for MyStore {
+///     // implement custom ranges handling
+///     async fn get_ranges(
+///         &self,
+///         location: &Path,
+///         ranges: &[Range<u64>],
+///     ) -> Result<Vec<Bytes>> {
+///         ...
+///     }
+///
+///     ...
+/// }
+///
+/// struct Wrapper {
+///     inner: Arc<dyn ObjectStore>,
+/// }
+///
+/// #[async_trait]
+/// #[deny(clippy::missing_trait_methods)]
+/// impl ObjectStore for Wrapper {
+///     // If we would not implement this method,
+///     // we would get the trait default and not
+///     // use the actual implementation of `inner`.
+///     async fn get_ranges(
+///         &self,
+///         location: &Path,
+///         ranges: &[Range<u64>],
+///     ) -> Result<Vec<Bytes>> {
+///         ...
+///     }
+///
+///     ...
+/// }
+/// ```
+///
+/// To automatically detect this issue, use
+/// 
[`#[deny(clippy::missing_trait_methods)]`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods).
 #[async_trait]
 pub trait ObjectStore: std::fmt::Display + Send + Sync + Debug + 'static {
     /// Save the provided `payload` to `location` with the given options
@@ -1065,6 +1115,7 @@ pub trait ObjectStore: std::fmt::Display + Send + Sync + 
Debug + 'static {
 macro_rules! as_ref_impl {
     ($type:ty) => {
         #[async_trait]
+        #[deny(clippy::missing_trait_methods)]
         impl ObjectStore for $type {
             async fn put_opts(
                 &self,
diff --git a/src/limit.rs b/src/limit.rs
index 485a308..a46ba56 100644
--- a/src/limit.rs
+++ b/src/limit.rs
@@ -70,6 +70,7 @@ impl<T: ObjectStore> std::fmt::Display for LimitStore<T> {
 }
 
 #[async_trait]
+#[deny(clippy::missing_trait_methods)]
 impl<T: ObjectStore> ObjectStore for LimitStore<T> {
     async fn put_opts(
         &self,
diff --git a/src/prefix.rs b/src/prefix.rs
index 7b8d60d..19d2ada 100644
--- a/src/prefix.rs
+++ b/src/prefix.rs
@@ -93,6 +93,7 @@ fn strip_meta(prefix: &Path, meta: ObjectMeta) -> ObjectMeta {
 }
 
 #[async_trait::async_trait]
+#[deny(clippy::missing_trait_methods)]
 impl<T: ObjectStore> ObjectStore for PrefixStore<T> {
     async fn put_opts(
         &self,
diff --git a/src/throttle.rs b/src/throttle.rs
index d9c4855..8b6196a 100644
--- a/src/throttle.rs
+++ b/src/throttle.rs
@@ -147,6 +147,7 @@ impl<T: ObjectStore> std::fmt::Display for 
ThrottledStore<T> {
 }
 
 #[async_trait]
+#[deny(clippy::missing_trait_methods)]
 impl<T: ObjectStore> ObjectStore for ThrottledStore<T> {
     async fn put_opts(
         &self,

Reply via email to