use object_store::ObjectStore;
use object_store::path::Path;
use crate::{AsyncBackend, BackendResponse, PmtResult};
#[derive(Debug)]
pub struct ObjectStoreBackend {
store: Box<dyn ObjectStore>,
path: Path,
}
impl ObjectStoreBackend {
pub fn new<P: Into<Path>>(store: Box<dyn ObjectStore>, path: P) -> Self {
Self {
store,
path: path.into(),
}
}
#[must_use]
pub fn store(&self) -> &dyn ObjectStore {
&self.store
}
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
}
impl AsyncBackend for ObjectStoreBackend {
async fn read(&self, offset: usize, length: usize) -> PmtResult<BackendResponse> {
use object_store::{GetOptions, GetRange};
let opts = GetOptions {
range: Some(GetRange::Bounded(offset as u64..(offset + length) as u64)),
..Default::default()
};
let mut result = self.store.get_opts(&self.path, opts).await?;
let data_version = result
.meta
.e_tag
.take()
.or_else(|| Some(result.meta.last_modified.to_rfc3339()));
let bytes = result.bytes().await?;
Ok(match data_version {
Some(version) => BackendResponse::new_with_version(bytes, version),
None => BackendResponse::new(bytes),
})
}
}
#[cfg(test)]
mod tests {
use bytes::Bytes;
use object_store::memory::InMemory;
use object_store::path::Path;
use object_store::{ObjectStore, PutOptions, PutPayload};
use super::*;
use crate::PmtError;
#[test]
fn test_new_backend() {
let store = Box::new(InMemory::new());
let backend = ObjectStoreBackend::new(store, "test.pmtiles");
assert_eq!(backend.path().as_ref(), "test.pmtiles");
assert_eq!(backend.store().to_string(), "InMemory");
}
#[tokio::test]
async fn test_error_nonexistant() {
let store = Box::new(InMemory::new());
let backend = ObjectStoreBackend::new(store, "nonexistent.pmtiles");
let result = backend.read(0, 100).await;
assert!(matches!(
result.unwrap_err(),
PmtError::ObjectStore(object_store::Error::NotFound { .. })
));
}
#[tokio::test]
async fn test_read_returns_etag() {
let store = Box::new(InMemory::new());
let path = Path::from("test.bin");
let payload: PutPayload = Bytes::copy_from_slice(&[0u8; 64]).into();
store
.put_opts(&path, payload, PutOptions::default())
.await
.unwrap();
let backend = ObjectStoreBackend::new(store, path);
let response = backend.read(0, 64).await.unwrap();
assert!(response.data_version_string.is_some());
}
}