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,