#[macro_export]
macro_rules! delegate_to_inner_impl {
(
impl ObjectStore for $Type:ty {
forward: $($method:ident),* $(,)? ;
$($overrides:tt)*
}
) => {
$crate::delegate_to_inner_impl! {
@munch
ty: $Type;
overrides: { $($overrides)* };
acc: {};
remaining: [ $($method)* ];
}
};
(
@munch
ty: $Type:ty;
overrides: { $($overrides:tt)* };
acc: { $($acc:tt)* };
remaining: [];
) => {
#[::async_trait::async_trait]
impl $crate::object_store::ObjectStore for $Type {
$($overrides)*
$($acc)*
}
};
(
@munch
ty: $Type:ty;
overrides: { $($overrides:tt)* };
acc: { $($acc:tt)* };
remaining: [ list $($rest:ident)* ];
) => {
$crate::delegate_to_inner_impl! {
@munch
ty: $Type;
overrides: { $($overrides)* };
acc: {
$($acc)*
async fn list(
&self,
prefix: &str,
) -> ::std::result::Result<
::std::vec::Vec<$crate::object_store::ObjectMeta>,
$crate::object_store::ObjectStoreError,
> {
self.inner.list(prefix).await
}
};
remaining: [ $($rest)* ];
}
};
(
@munch
ty: $Type:ty;
overrides: { $($overrides:tt)* };
acc: { $($acc:tt)* };
remaining: [ get_to_file $($rest:ident)* ];
) => {
$crate::delegate_to_inner_impl! {
@munch
ty: $Type;
overrides: { $($overrides)* };
acc: {
$($acc)*
async fn get_to_file(
&self,
key: &str,
dest: &::std::path::Path,
opts: $crate::object_store::GetOpts,
) -> ::std::result::Result<(), $crate::object_store::ObjectStoreError> {
self.inner.get_to_file(key, dest, opts).await
}
};
remaining: [ $($rest)* ];
}
};
(
@munch
ty: $Type:ty;
overrides: { $($overrides:tt)* };
acc: { $($acc:tt)* };
remaining: [ get_bytes $($rest:ident)* ];
) => {
$crate::delegate_to_inner_impl! {
@munch
ty: $Type;
overrides: { $($overrides)* };
acc: {
$($acc)*
async fn get_bytes(
&self,
key: &str,
) -> ::std::result::Result<
::bytes::Bytes,
$crate::object_store::ObjectStoreError,
> {
self.inner.get_bytes(key).await
}
};
remaining: [ $($rest)* ];
}
};
(
@munch
ty: $Type:ty;
overrides: { $($overrides:tt)* };
acc: { $($acc:tt)* };
remaining: [ get_bytes_range $($rest:ident)* ];
) => {
$crate::delegate_to_inner_impl! {
@munch
ty: $Type;
overrides: { $($overrides)* };
acc: {
$($acc)*
async fn get_bytes_range(
&self,
key: &str,
range: ::std::ops::Range<u64>,
) -> ::std::result::Result<
::bytes::Bytes,
$crate::object_store::ObjectStoreError,
> {
self.inner.get_bytes_range(key, range).await
}
};
remaining: [ $($rest)* ];
}
};
(
@munch
ty: $Type:ty;
overrides: { $($overrides:tt)* };
acc: { $($acc:tt)* };
remaining: [ put_bytes $($rest:ident)* ];
) => {
$crate::delegate_to_inner_impl! {
@munch
ty: $Type;
overrides: { $($overrides)* };
acc: {
$($acc)*
async fn put_bytes(
&self,
key: &str,
body: ::bytes::Bytes,
opts: $crate::object_store::PutOpts,
) -> ::std::result::Result<(), $crate::object_store::ObjectStoreError> {
self.inner.put_bytes(key, body, opts).await
}
};
remaining: [ $($rest)* ];
}
};
(
@munch
ty: $Type:ty;
overrides: { $($overrides:tt)* };
acc: { $($acc:tt)* };
remaining: [ put_path $($rest:ident)* ];
) => {
$crate::delegate_to_inner_impl! {
@munch
ty: $Type;
overrides: { $($overrides)* };
acc: {
$($acc)*
async fn put_path(
&self,
key: &str,
src: &::std::path::Path,
opts: $crate::object_store::PutOpts,
) -> ::std::result::Result<(), $crate::object_store::ObjectStoreError> {
self.inner.put_path(key, src, opts).await
}
};
remaining: [ $($rest)* ];
}
};
(
@munch
ty: $Type:ty;
overrides: { $($overrides:tt)* };
acc: { $($acc:tt)* };
remaining: [ put_if_absent $($rest:ident)* ];
) => {
$crate::delegate_to_inner_impl! {
@munch
ty: $Type;
overrides: { $($overrides)* };
acc: {
$($acc)*
async fn put_if_absent(
&self,
key: &str,
body: ::bytes::Bytes,
) -> ::std::result::Result<bool, $crate::object_store::ObjectStoreError> {
self.inner.put_if_absent(key, body).await
}
};
remaining: [ $($rest)* ];
}
};
(
@munch
ty: $Type:ty;
overrides: { $($overrides:tt)* };
acc: { $($acc:tt)* };
remaining: [ head $($rest:ident)* ];
) => {
$crate::delegate_to_inner_impl! {
@munch
ty: $Type;
overrides: { $($overrides)* };
acc: {
$($acc)*
async fn head(
&self,
key: &str,
) -> ::std::result::Result<
$crate::object_store::ObjectMeta,
$crate::object_store::ObjectStoreError,
> {
self.inner.head(key).await
}
};
remaining: [ $($rest)* ];
}
};
(
@munch
ty: $Type:ty;
overrides: { $($overrides:tt)* };
acc: { $($acc:tt)* };
remaining: [ copy $($rest:ident)* ];
) => {
$crate::delegate_to_inner_impl! {
@munch
ty: $Type;
overrides: { $($overrides)* };
acc: {
$($acc)*
async fn copy(
&self,
src: &str,
dst: &str,
) -> ::std::result::Result<(), $crate::object_store::ObjectStoreError> {
self.inner.copy(src, dst).await
}
};
remaining: [ $($rest)* ];
}
};
(
@munch
ty: $Type:ty;
overrides: { $($overrides:tt)* };
acc: { $($acc:tt)* };
remaining: [ delete $($rest:ident)* ];
) => {
$crate::delegate_to_inner_impl! {
@munch
ty: $Type;
overrides: { $($overrides)* };
acc: {
$($acc)*
async fn delete(
&self,
key: &str,
) -> ::std::result::Result<(), $crate::object_store::ObjectStoreError> {
self.inner.delete(key).await
}
};
remaining: [ $($rest)* ];
}
};
(
@munch
ty: $Type:ty;
overrides: { $($overrides:tt)* };
acc: { $($acc:tt)* };
remaining: [ presigned_get_url $($rest:ident)* ];
) => {
$crate::delegate_to_inner_impl! {
@munch
ty: $Type;
overrides: { $($overrides)* };
acc: {
$($acc)*
async fn presigned_get_url(
&self,
key: &str,
ttl: ::std::time::Duration,
) -> ::std::result::Result<
::std::string::String,
$crate::object_store::ObjectStoreError,
> {
self.inner.presigned_get_url(key, ttl).await
}
};
remaining: [ $($rest)* ];
}
};
}
#[cfg(test)]
mod tests {
use bytes::Bytes;
use crate::object_store::ObjectStore;
use crate::object_store::mock::MockStore;
struct RecordingHead {
inner: MockStore,
seen: std::sync::Mutex<Vec<String>>,
}
crate::delegate_to_inner_impl! {
impl ObjectStore for RecordingHead {
forward: list, get_to_file, get_bytes, get_bytes_range,
put_bytes, put_path, put_if_absent,
copy, delete, presigned_get_url;
async fn head(
&self,
key: &str,
) -> Result<
crate::object_store::ObjectMeta,
crate::object_store::ObjectStoreError,
> {
self.seen.lock().unwrap().push(key.to_owned());
self.inner.head(key).await
}
}
}
#[tokio::test]
async fn override_records_intercepted_method_and_forwards_others() {
let inner = MockStore::new();
inner.insert("k", Bytes::from_static(b"v"));
let store = RecordingHead {
inner,
seen: std::sync::Mutex::new(Vec::new()),
};
let body = store.get_bytes("k").await.expect("forwarded get_bytes");
assert_eq!(body.as_ref(), b"v");
let _ = store.head("k").await.expect("forwarded head");
assert_eq!(store.seen.lock().unwrap().as_slice(), &["k".to_owned()]);
}
struct PassThrough {
inner: MockStore,
}
crate::delegate_to_inner_impl! {
impl ObjectStore for PassThrough {
forward: list, get_to_file, get_bytes, get_bytes_range,
put_bytes, put_path, put_if_absent,
head, copy, delete, presigned_get_url;
}
}
#[tokio::test]
async fn no_overrides_forwards_every_method_to_inner() {
let inner = MockStore::new();
inner.insert("k", Bytes::from_static(b"v"));
let store = PassThrough { inner };
let body = store.get_bytes("k").await.expect("forwarded get_bytes");
assert_eq!(body.as_ref(), b"v");
let entries = store.list("").await.expect("forwarded list");
assert_eq!(entries.len(), 1);
}
}