use std::fmt::{Display, Formatter};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use async_trait::async_trait;
use futures::stream::BoxStream;
use object_store::path::Path;
use object_store::{
CopyOptions, GetOptions, GetResult, ListResult, MultipartUpload, ObjectMeta, ObjectStore,
PutMultipartOptions, PutOptions, PutPayload, PutResult, Result,
};
#[derive(Debug, Default)]
pub(crate) struct RequestCounts {
gets: AtomicUsize,
heads: AtomicUsize,
}
impl RequestCounts {
pub(crate) fn gets(&self) -> usize {
self.gets.load(Ordering::Relaxed)
}
pub(crate) fn heads(&self) -> usize {
self.heads.load(Ordering::Relaxed)
}
}
#[derive(Debug)]
pub(crate) struct CountingObjectStore {
inner: Arc<dyn ObjectStore>,
counts: Arc<RequestCounts>,
}
impl CountingObjectStore {
pub(crate) fn new(inner: Arc<dyn ObjectStore>) -> (Arc<Self>, Arc<RequestCounts>) {
let counts = Arc::new(RequestCounts::default());
(
Arc::new(Self {
inner,
counts: counts.clone(),
}),
counts,
)
}
}
impl Display for CountingObjectStore {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "CountingObjectStore({})", self.inner)
}
}
#[async_trait]
impl ObjectStore for CountingObjectStore {
async fn put_opts(
&self,
location: &Path,
payload: PutPayload,
opts: PutOptions,
) -> Result<PutResult> {
self.inner.put_opts(location, payload, opts).await
}
async fn put_multipart_opts(
&self,
location: &Path,
opts: PutMultipartOptions,
) -> Result<Box<dyn MultipartUpload>> {
self.inner.put_multipart_opts(location, opts).await
}
async fn get_opts(&self, location: &Path, options: GetOptions) -> Result<GetResult> {
if options.head {
self.counts.heads.fetch_add(1, Ordering::Relaxed);
} else {
self.counts.gets.fetch_add(1, Ordering::Relaxed);
}
self.inner.get_opts(location, options).await
}
fn delete_stream(
&self,
locations: BoxStream<'static, Result<Path>>,
) -> BoxStream<'static, Result<Path>> {
self.inner.delete_stream(locations)
}
fn list(&self, prefix: Option<&Path>) -> BoxStream<'static, Result<ObjectMeta>> {
self.inner.list(prefix)
}
async fn list_with_delimiter(&self, prefix: Option<&Path>) -> Result<ListResult> {
self.inner.list_with_delimiter(prefix).await
}
async fn copy_opts(&self, from: &Path, to: &Path, options: CopyOptions) -> Result<()> {
self.inner.copy_opts(from, to, options).await
}
}