This is an automated email from the ASF dual-hosted git repository.
alamb 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 52f5119 Add `Extensions` in `*Result` objects (#743)
52f5119 is described below
commit 52f511967122dfce81bcd0009be4957c0de7ce7a
Author: Chris <[email protected]>
AuthorDate: Fri Jun 12 04:03:39 2026 -0700
Add `Extensions` in `*Result` objects (#743)
* add extensions support to results
* add extensions to MultiStatus
* fmt
* rustdocs
* get rid of extensions in paginated list result
* add extension result integration tests
---
src/aws/client.rs | 30 ++++++++++++------
src/aws/mod.rs | 6 +++-
src/azure/client.rs | 25 ++++++++++-----
src/azure/mod.rs | 8 ++++-
src/client/get.rs | 1 +
src/client/header.rs | 17 +++++++---
src/client/list.rs | 3 ++
src/client/s3.rs | 1 +
src/gcp/client.rs | 29 +++++++++++------
src/gcp/mod.rs | 11 ++++++-
src/http/client.rs | 21 ++++++++-----
src/http/mod.rs | 9 +++++-
src/integration.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/lib.rs | 42 ++++++++++++++++++++++++-
src/local.rs | 4 +++
src/memory.rs | 5 +++
src/prefix.rs | 1 +
src/upload.rs | 1 +
18 files changed, 260 insertions(+), 43 deletions(-)
diff --git a/src/aws/client.rs b/src/aws/client.rs
index 199f4a8..d6b2534 100644
--- a/src/aws/client.rs
+++ b/src/aws/client.rs
@@ -36,8 +36,8 @@ use crate::client::{GetOptionsExt, HttpClient, HttpError,
HttpResponse};
use crate::list::{PaginatedListOptions, PaginatedListResult};
use crate::multipart::PartId;
use crate::{
- Attribute, Attributes, ClientOptions, GetOptions, MultipartId, Path,
PutMultipartOptions,
- PutPayload, PutResult, Result, RetryConfig, TagSet,
+ Attribute, Attributes, ClientOptions, GetOptions, ListResult, MultipartId,
Path,
+ PutMultipartOptions, PutPayload, PutResult, Result, RetryConfig, TagSet,
};
use async_trait::async_trait;
use base64::Engine;
@@ -470,8 +470,10 @@ impl Request<'_> {
pub(crate) async fn do_put(self) -> Result<PutResult> {
let response = self.send().await?;
- Ok(get_put_result(response.headers(), VERSION_HEADER)
- .map_err(|source| Error::Metadata { source })?)
+ Ok(
+ get_put_result(response, VERSION_HEADER)
+ .map_err(|source| Error::Metadata { source })?,
+ )
}
}
@@ -857,11 +859,12 @@ impl S3Client {
path: location.as_ref().to_string(),
})?;
- let version = get_version(response.headers(), VERSION_HEADER)
+ let (parts, body) = response.into_parts();
+
+ let version = get_version(&parts.headers, VERSION_HEADER)
.map_err(|source| Error::Metadata { source })?;
- let data = response
- .into_body()
+ let data = body
.bytes()
.await
.map_err(|source| Error::CompleteMultipartResponseBody { source
})?;
@@ -872,6 +875,7 @@ impl S3Client {
Ok(PutResult {
e_tag: Some(response.e_tag),
version,
+ extensions: parts.extensions,
})
}
@@ -993,8 +997,11 @@ impl ListClient for Arc<S3Client> {
.with_aws_sigv4(credential.authorizer(), None)
.send_retry(&self.config.retry_config)
.await
- .map_err(|source| Error::ListRequest { source })?
- .into_body()
+ .map_err(|source| Error::ListRequest { source })?;
+
+ let (parts, body) = response.into_parts();
+
+ let response = body
.bytes()
.await
.map_err(|source| Error::ListResponseBody { source })?;
@@ -1004,8 +1011,11 @@ impl ListClient for Arc<S3Client> {
let token = response.next_continuation_token.take();
+ let mut result: ListResult = response.try_into()?;
+ result.extensions = parts.extensions;
+
Ok(PaginatedListResult {
- result: response.try_into()?,
+ result,
page_token: token,
})
}
diff --git a/src/aws/mod.rs b/src/aws/mod.rs
index e1cdb06..765871a 100644
--- a/src/aws/mod.rs
+++ b/src/aws/mod.rs
@@ -679,7 +679,10 @@ mod tests {
#[tokio::test]
async fn s3_test() {
maybe_skip_integration!();
- let config = AmazonS3Builder::from_env();
+ // tag the extensions of every HTTP response with a marker,
+ // allowing response_extensions to verify their propagation
+ let config =
+
AmazonS3Builder::from_env().with_http_connector(MarkerHttpConnector::default());
let integration = config.build().unwrap();
let config = &integration.client.config;
@@ -701,6 +704,7 @@ mod tests {
s3_encryption(&integration).await;
put_get_attributes(&integration).await;
list_paginated(&integration, &integration).await;
+ response_extensions(&integration, true).await;
// Object tagging is not supported by S3 Express One Zone
if config.session_provider.is_none() {
diff --git a/src/azure/client.rs b/src/azure/client.rs
index 6f1ad23..fd3e923 100644
--- a/src/azure/client.rs
+++ b/src/azure/client.rs
@@ -564,8 +564,10 @@ impl AzureClient {
};
let response = builder.header(&BLOB_TYPE, "BlockBlob").send().await?;
- Ok(get_put_result(response.headers(), VERSION_HEADER)
- .map_err(|source| Error::Metadata { source })?)
+ Ok(
+ get_put_result(response, VERSION_HEADER)
+ .map_err(|source| Error::Metadata { source })?,
+ )
}
/// PUT a block
<https://learn.microsoft.com/en-us/rest/api/storageservices/put-block>
@@ -617,8 +619,10 @@ impl AzureClient {
.send()
.await?;
- Ok(get_put_result(response.headers(), VERSION_HEADER)
- .map_err(|source| Error::Metadata { source })?)
+ Ok(
+ get_put_result(response, VERSION_HEADER)
+ .map_err(|source| Error::Metadata { source })?,
+ )
}
fn build_bulk_delete_body(
@@ -983,8 +987,11 @@ impl ListClient for Arc<AzureClient> {
.sensitive(sensitive)
.send()
.await
- .map_err(|source| Error::ListRequest { source })?
- .into_body()
+ .map_err(|source| Error::ListRequest { source })?;
+
+ let (parts, body) = response.into_parts();
+
+ let response = body
.bytes()
.await
.map_err(|source| Error::ListResponseBody { source })?;
@@ -1007,8 +1014,11 @@ impl ListClient for Arc<AzureClient> {
}
}
+ let mut result = to_list_result(response, prefix)?;
+ result.extensions = parts.extensions;
+
Ok(PaginatedListResult {
- result: to_list_result(response, prefix)?,
+ result,
page_token: token,
})
}
@@ -1051,6 +1061,7 @@ fn to_list_result(value: ListResultInternal, prefix:
Option<&str>) -> Result<Lis
Ok(ListResult {
common_prefixes,
objects,
+ extensions: Default::default(),
})
}
diff --git a/src/azure/mod.rs b/src/azure/mod.rs
index e2ed05f..6d7b642 100644
--- a/src/azure/mod.rs
+++ b/src/azure/mod.rs
@@ -341,7 +341,12 @@ mod tests {
#[tokio::test]
async fn azure_blob_test() {
maybe_skip_integration!();
- let integration = MicrosoftAzureBuilder::from_env().build().unwrap();
+ // tag the extensions of every HTTP response with a marker,
+ // allowing response_extensions to verify their propagation
+ let integration = MicrosoftAzureBuilder::from_env()
+ .with_http_connector(MarkerHttpConnector::default())
+ .build()
+ .unwrap();
put_get_delete_list(&integration).await;
list_with_offset_exclusivity(&integration).await;
@@ -358,6 +363,7 @@ mod tests {
multipart_out_of_order(&integration).await;
signing(&integration).await;
list_paginated(&integration, &integration).await;
+ response_extensions(&integration, true).await;
let validate = !integration.client.config().disable_tagging;
tagging(
diff --git a/src/client/get.rs b/src/client/get.rs
index 044919b..a9de711 100644
--- a/src/client/get.rs
+++ b/src/client/get.rs
@@ -192,6 +192,7 @@ impl<T: GetClient> GetContext<T> {
meta,
range,
attributes,
+ extensions: parts.extensions,
})
}
diff --git a/src/client/header.rs b/src/client/header.rs
index 4c9470c..d0dd27a 100644
--- a/src/client/header.rs
+++ b/src/client/header.rs
@@ -70,15 +70,22 @@ pub(crate) enum Error {
},
}
-/// Extracts a PutResult from the provided [`HeaderMap`]
+/// Extracts a PutResult from the provided response
+///
+/// Propagates the extensions of the response into the [`crate::PutResult`]
#[cfg(any(feature = "aws", feature = "gcp", feature = "azure"))]
pub(crate) fn get_put_result(
- headers: &HeaderMap,
+ response: crate::client::HttpResponse,
version: &str,
) -> Result<crate::PutResult, Error> {
- let e_tag = Some(get_etag(headers)?);
- let version = get_version(headers, version)?;
- Ok(crate::PutResult { e_tag, version })
+ let (parts, _) = response.into_parts();
+ let e_tag = Some(get_etag(&parts.headers)?);
+ let version = get_version(&parts.headers, version)?;
+ Ok(crate::PutResult {
+ e_tag,
+ version,
+ extensions: parts.extensions,
+ })
}
/// Extracts a optional version from the provided [`HeaderMap`]
diff --git a/src/client/list.rs b/src/client/list.rs
index c212318..e0d684d 100644
--- a/src/client/list.rs
+++ b/src/client/list.rs
@@ -114,16 +114,19 @@ impl<T: ListClient + Clone> ListClientExt for T {
let mut common_prefixes = BTreeSet::new();
let mut objects = Vec::new();
+ let mut extensions = http::Extensions::new();
while let Some(result) = stream.next().await {
let response = result?;
common_prefixes.extend(response.common_prefixes);
objects.extend(response.objects);
+ extensions.extend(response.extensions);
}
Ok(ListResult {
common_prefixes: common_prefixes.into_iter().collect(),
objects,
+ extensions,
})
}
}
diff --git a/src/client/s3.rs b/src/client/s3.rs
index 2beec61..2c8fb63 100644
--- a/src/client/s3.rs
+++ b/src/client/s3.rs
@@ -52,6 +52,7 @@ impl TryFrom<ListResponse> for ListResult {
Ok(Self {
common_prefixes,
objects,
+ extensions: Default::default(),
})
}
}
diff --git a/src/gcp/client.rs b/src/gcp/client.rs
index 4a246d2..5356cc4 100644
--- a/src/gcp/client.rs
+++ b/src/gcp/client.rs
@@ -32,7 +32,7 @@ use crate::multipart::PartId;
use crate::path::Path;
use crate::util::hex_encode;
use crate::{
- Attribute, Attributes, ClientOptions, CopyMode, GetOptions, MultipartId,
PutMode,
+ Attribute, Attributes, ClientOptions, CopyMode, GetOptions, ListResult,
MultipartId, PutMode,
PutMultipartOptions, PutOptions, PutPayload, PutResult, Result,
RetryConfig,
};
use async_trait::async_trait;
@@ -250,8 +250,10 @@ impl Request<'_> {
async fn do_put(self) -> Result<PutResult> {
let response = self.send().await?;
- Ok(get_put_result(response.headers(), VERSION_HEADER)
- .map_err(|source| Error::Metadata { source })?)
+ Ok(
+ get_put_result(response, VERSION_HEADER)
+ .map_err(|source| Error::Metadata { source })?,
+ )
}
}
@@ -541,11 +543,12 @@ impl GoogleCloudStorageClient {
.await
.map_err(|source| Error::CompleteMultipartRequest { source })?;
- let version = get_version(response.headers(), VERSION_HEADER)
+ let (parts, body) = response.into_parts();
+
+ let version = get_version(&parts.headers, VERSION_HEADER)
.map_err(|source| Error::Metadata { source })?;
- let data = response
- .into_body()
+ let data = body
.bytes()
.await
.map_err(|source| Error::CompleteMultipartResponseBody { source
})?;
@@ -556,6 +559,7 @@ impl GoogleCloudStorageClient {
Ok(PutResult {
e_tag: Some(response.e_tag),
version,
+ extensions: parts.extensions,
})
}
@@ -705,8 +709,11 @@ impl ListClient for Arc<GoogleCloudStorageClient> {
.with_bearer_auth(credential.as_deref())
.send_retry(&self.config.retry_config)
.await
- .map_err(|source| Error::ListRequest { source })?
- .into_body()
+ .map_err(|source| Error::ListRequest { source })?;
+
+ let (parts, body) = response.into_parts();
+
+ let response = body
.bytes()
.await
.map_err(|source| Error::ListResponseBody { source })?;
@@ -715,8 +722,12 @@ impl ListClient for Arc<GoogleCloudStorageClient> {
.map_err(|source| Error::InvalidListResponse { source })?;
let token = response.next_continuation_token.take();
+
+ let mut result: ListResult = response.try_into()?;
+ result.extensions = parts.extensions;
+
Ok(PaginatedListResult {
- result: response.try_into()?,
+ result,
page_token: token,
})
}
diff --git a/src/gcp/mod.rs b/src/gcp/mod.rs
index 51e85ae..7dc22ad 100644
--- a/src/gcp/mod.rs
+++ b/src/gcp/mod.rs
@@ -311,7 +311,12 @@ mod test {
#[tokio::test]
async fn gcs_test() {
maybe_skip_integration!();
- let integration =
GoogleCloudStorageBuilder::from_env().build().unwrap();
+ // tag the extensions of every HTTP response with a marker,
+ // allowing response_extensions to verify their propagation
+ let integration = GoogleCloudStorageBuilder::from_env()
+ .with_http_connector(MarkerHttpConnector::default())
+ .build()
+ .unwrap();
put_get_delete_list(&integration).await;
list_with_offset_exclusivity(&integration).await;
@@ -336,6 +341,10 @@ mod test {
// Fake GCS server doesn't currently support attributes
put_get_attributes(&integration).await;
}
+
+ // Fake GCS server does not yet implement XML Multipart uploads
+ let test_multipart = integration.client.config().base_url ==
DEFAULT_GCS_BASE_URL;
+ response_extensions(&integration, test_multipart).await;
}
#[tokio::test]
diff --git a/src/http/client.rs b/src/http/client.rs
index 5b2d343..4375a79 100644
--- a/src/http/client.rs
+++ b/src/http/client.rs
@@ -256,12 +256,15 @@ impl Client {
.send()
.await;
- let response = match result {
- Ok(result) => result
- .into_body()
- .bytes()
- .await
- .map_err(|source| Error::Reqwest { source })?,
+ let (response, extensions) = match result {
+ Ok(result) => {
+ let (parts, body) = result.into_parts();
+ let body = body
+ .bytes()
+ .await
+ .map_err(|source| Error::Reqwest { source })?;
+ (body, parts.extensions)
+ }
Err(e) if matches!(e.status(), Some(StatusCode::NOT_FOUND)) => {
return match depth {
"0" => {
@@ -286,8 +289,9 @@ impl Client {
}
};
- let status = quick_xml::de::from_reader(response.reader())
+ let mut status: MultiStatus =
quick_xml::de::from_reader(response.reader())
.map_err(|source| Error::InvalidPropFind { source })?;
+ status.extensions = extensions;
Ok(status)
}
@@ -415,6 +419,9 @@ impl GetClient for Client {
#[derive(Deserialize, Default)]
pub(crate) struct MultiStatus {
pub response: Vec<MultiStatusResponse>,
+ /// The extensions of the HTTP response this was parsed from
+ #[serde(skip)]
+ pub extensions: ::http::Extensions,
}
#[derive(Deserialize)]
diff --git a/src/http/mod.rs b/src/http/mod.rs
index 4d4081b..3fbf162 100644
--- a/src/http/mod.rs
+++ b/src/http/mod.rs
@@ -111,7 +111,8 @@ impl ObjectStore for HttpStore {
}
let response = self.client.put(location, payload,
opts.attributes).await?;
- let e_tag = match get_etag(response.headers()) {
+ let (parts, _) = response.into_parts();
+ let e_tag = match get_etag(&parts.headers) {
Ok(e_tag) => Some(e_tag),
Err(crate::client::header::Error::MissingEtag) => None,
Err(source) => return Err(Error::Metadata { source }.into()),
@@ -120,6 +121,7 @@ impl ObjectStore for HttpStore {
Ok(PutResult {
e_tag,
version: None,
+ extensions: parts.extensions,
})
}
@@ -209,6 +211,7 @@ impl ObjectStore for HttpStore {
Ok(ListResult {
common_prefixes,
objects,
+ extensions: status.extensions,
})
}
@@ -302,9 +305,12 @@ mod tests {
maybe_skip_integration!();
let url = std::env::var("HTTP_URL").expect("HTTP_URL must be set");
let options = ClientOptions::new().with_allow_http(true);
+ // tag the extensions of every HTTP response with a marker,
+ // allowing response_extensions to verify their propagation
let integration = HttpBuilder::new()
.with_url(url)
.with_client_options(options)
+ .with_http_connector(MarkerHttpConnector::default())
.build()
.unwrap();
@@ -313,5 +319,6 @@ mod tests {
list_with_delimiter(&integration).await;
rename_and_copy(&integration).await;
copy_if_not_exists(&integration).await;
+ response_extensions(&integration, false).await;
}
}
diff --git a/src/integration.rs b/src/integration.rs
index e68837c..8b435d3 100644
--- a/src/integration.rs
+++ b/src/integration.rs
@@ -1436,3 +1436,92 @@ pub async fn list_with_offset_exclusivity(storage:
&DynObjectStore) {
// Clean up
delete_fixtures(storage).await;
}
+
+#[cfg(all(
+ feature = "cloud",
+ not(all(target_arch = "wasm32", target_os = "wasi"))
+))]
+mod marker {
+ use super::*;
+ use crate::ClientOptions;
+ use crate::client::{
+ HttpClient, HttpConnector, HttpError, HttpRequest, HttpResponse,
HttpService,
+ ReqwestConnector,
+ };
+ use async_trait::async_trait;
+
+ /// A marker inserted into response extensions by [`MarkerHttpConnector`]
+ #[derive(Clone, Debug, PartialEq)]
+ struct Marker;
+
+ /// [`HttpService`] middleware that tags every response with a [`Marker`]
+ #[derive(Debug)]
+ struct MarkerService(HttpClient);
+
+ #[async_trait]
+ impl HttpService for MarkerService {
+ async fn call(&self, req: HttpRequest) -> Result<HttpResponse,
HttpError> {
+ let mut response = self.0.execute(req).await?;
+ response.extensions_mut().insert(Marker);
+ Ok(response)
+ }
+ }
+
+ /// An [`HttpConnector`] that tags the extensions of every HTTP response
with a
+ /// marker, allowing [`response_extensions`] to verify their propagation
+ #[derive(Debug, Default)]
+ pub struct MarkerHttpConnector(ReqwestConnector);
+
+ impl HttpConnector for MarkerHttpConnector {
+ fn connect(&self, options: &ClientOptions) ->
crate::Result<HttpClient> {
+ let client = self.0.connect(options)?;
+ Ok(HttpClient::new(MarkerService(client)))
+ }
+ }
+
+ /// Tests that the extensions of HTTP responses are propagated to returned
results
+ ///
+ /// The provided store must have been built with [`MarkerHttpConnector`]
+ pub async fn response_extensions(storage: &DynObjectStore, test_multipart:
bool) {
+ delete_fixtures(storage).await;
+
+ let location = Path::from("test_response_extensions/file.txt");
+ let data = Bytes::from("arbitrary data");
+
+ let put = storage.put(&location, data.clone().into()).await.unwrap();
+ assert!(
+ put.extensions.get::<Marker>().is_some(),
+ "PutResult should contain the response extensions"
+ );
+
+ let get = storage.get(&location).await.unwrap();
+ assert!(
+ get.extensions.get::<Marker>().is_some(),
+ "GetResult should contain the response extensions"
+ );
+
+ let list = storage.list_with_delimiter(None).await.unwrap();
+ assert!(
+ list.extensions.get::<Marker>().is_some(),
+ "ListResult should contain the response extensions"
+ );
+
+ if test_multipart {
+ let mut upload = storage.put_multipart(&location).await.unwrap();
+ upload.put_part(data.into()).await.unwrap();
+ let complete = upload.complete().await.unwrap();
+ assert!(
+ complete.extensions.get::<Marker>().is_some(),
+ "PutResult of a completed multipart upload should contain the
response extensions"
+ );
+ }
+
+ delete_fixtures(storage).await;
+ }
+}
+
+#[cfg(all(
+ feature = "cloud",
+ not(all(target_arch = "wasm32", target_os = "wasi"))
+))]
+pub use marker::{MarkerHttpConnector, response_extensions};
diff --git a/src/lib.rs b/src/lib.rs
index d3ea9ee..cb286f0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1413,6 +1413,14 @@ pub struct ListResult {
pub common_prefixes: Vec<Path>,
/// Object metadata for the listing
pub objects: Vec<ObjectMeta>,
+ /// Implementation-specific extensions. Intended for use by
[`ObjectStore`] implementations
+ /// that need to return context-specific information (like cache status)
from trait methods.
+ ///
+ /// HTTP-backed stores in this crate populate this with the extensions of
the HTTP
+ /// response, allowing custom HTTP middleware to propagate information to
callers.
+ /// Where a result is assembled from multiple paginated requests, the
extensions of
+ /// each response are merged, with those of later responses taking
precedence.
+ pub extensions: Extensions,
}
/// The metadata that describes an object.
@@ -1627,6 +1635,12 @@ pub struct GetResult {
pub range: Range<u64>,
/// Additional object attributes
pub attributes: Attributes,
+ /// Implementation-specific extensions. Intended for use by
[`ObjectStore`] implementations
+ /// that need to return context-specific information (like cache status)
from trait methods.
+ ///
+ /// HTTP-backed stores in this crate populate this with the extensions of
the HTTP
+ /// response, allowing custom HTTP middleware to propagate information to
callers.
+ pub extensions: Extensions,
}
/// The kind of a [`GetResult`]
@@ -1865,7 +1879,7 @@ impl From<Attributes> for PutMultipartOptions {
}
/// Result for a put request
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone)]
pub struct PutResult {
/// The unique identifier for the newly created object
///
@@ -1873,8 +1887,34 @@ pub struct PutResult {
pub e_tag: Option<String>,
/// A version indicator for the newly created object
pub version: Option<String>,
+ /// Implementation-specific extensions. Intended for use by
[`ObjectStore`] implementations
+ /// that need to return context-specific information (like cache status)
from trait methods.
+ ///
+ /// HTTP-backed stores in this crate populate this with the extensions of
the HTTP
+ /// response, allowing custom HTTP middleware to propagate information to
callers.
+ ///
+ /// These extensions are excluded from [`PartialEq`] and [`Eq`].
+ pub extensions: Extensions,
}
+impl PartialEq<Self> for PutResult {
+ fn eq(&self, other: &Self) -> bool {
+ let Self {
+ e_tag,
+ version,
+ extensions: _,
+ } = self;
+ let Self {
+ e_tag: other_e_tag,
+ version: other_version,
+ extensions: _,
+ } = other;
+ (e_tag == other_e_tag) && (version == other_version)
+ }
+}
+
+impl Eq for PutResult {}
+
/// Configure preconditions for the copy operation
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CopyMode {
diff --git a/src/local.rs b/src/local.rs
index 2e71ce6..b3f3f7f 100644
--- a/src/local.rs
+++ b/src/local.rs
@@ -423,6 +423,7 @@ impl ObjectStore for LocalFileSystem {
Ok(PutResult {
e_tag,
version: None,
+ extensions: Default::default(),
})
})
.await
@@ -466,6 +467,7 @@ impl ObjectStore for LocalFileSystem {
attributes: Attributes::default(),
range,
meta,
+ extensions: Default::default(),
})
})
.await
@@ -563,6 +565,7 @@ impl ObjectStore for LocalFileSystem {
Ok(ListResult {
common_prefixes: common_prefixes.into_iter().collect(),
objects,
+ extensions: Default::default(),
})
})
.await
@@ -933,6 +936,7 @@ impl MultipartUpload for LocalUpload {
Ok(PutResult {
e_tag: Some(get_etag(&metadata)),
version: None,
+ extensions: Default::default(),
})
})
.await
diff --git a/src/memory.rs b/src/memory.rs
index 383c553..abc08d5 100644
--- a/src/memory.rs
+++ b/src/memory.rs
@@ -218,6 +218,7 @@ impl ObjectStore for InMemory {
Ok(PutResult {
e_tag: Some(etag.to_string()),
version: None,
+ extensions: Default::default(),
})
}
@@ -266,6 +267,7 @@ impl ObjectStore for InMemory {
attributes: entry.attributes,
meta,
range,
+ extensions: Default::default(),
})
}
@@ -383,6 +385,7 @@ impl ObjectStore for InMemory {
Ok(ListResult {
objects,
common_prefixes: common_prefixes.into_iter().collect(),
+ extensions: Default::default(),
})
}
@@ -463,6 +466,7 @@ impl MultipartStore for InMemory {
Ok(PutResult {
e_tag: Some(etag.to_string()),
version: None,
+ extensions: Default::default(),
})
}
@@ -529,6 +533,7 @@ impl MultipartUpload for InMemoryUpload {
Ok(PutResult {
e_tag: Some(etag.to_string()),
version: None,
+ extensions: Default::default(),
})
}
diff --git a/src/prefix.rs b/src/prefix.rs
index 46cd681..9b3cf19 100644
--- a/src/prefix.rs
+++ b/src/prefix.rs
@@ -186,6 +186,7 @@ impl<T: ObjectStore> ObjectStore for PrefixStore<T> {
.into_iter()
.map(|meta| self.strip_meta(meta))
.collect(),
+ extensions: lst.extensions,
})
}
diff --git a/src/upload.rs b/src/upload.rs
index 01e6b28..b5bc940 100644
--- a/src/upload.rs
+++ b/src/upload.rs
@@ -294,6 +294,7 @@ mod tests {
Ok(PutResult {
e_tag: None,
version: None,
+ extensions: Default::default(),
})
}