This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new 03d6c7734 feat(core): Expose presign_xxx_options API (#6273)
03d6c7734 is described below

commit 03d6c77341ab82f0fc0bd6aacc139d0bd13da0a1
Author: Drew Gallardo <[email protected]>
AuthorDate: Tue Jun 10 03:14:12 2025 -0700

    feat(core): Expose presign_xxx_options API (#6273)
    
    * Expose presign_xxx_options API
    
    * remove presign option tests
    
    * remove options change to s3
---
 core/src/types/operator/operator.rs         | 324 +++++++++++++++++++---------
 core/src/types/operator/operator_futures.rs | 118 +++++-----
 2 files changed, 281 insertions(+), 161 deletions(-)

diff --git a/core/src/types/operator/operator.rs 
b/core/src/types/operator/operator.rs
index 08f8e492f..196d33221 100644
--- a/core/src/types/operator/operator.rs
+++ b/core/src/types/operator/operator.rs
@@ -1652,15 +1652,63 @@ impl Operator {
         OperatorFuture::new(
             self.inner().clone(),
             path,
-            (OpStat::default(), expire),
-            |inner, path, (args, dur)| async move {
-                let op = OpPresign::new(args, dur);
-                let rp = inner.presign(&path, op).await?;
-                Ok(rp.into_presigned_request())
-            },
+            (options::StatOptions::default(), expire),
+            Self::presign_stat_inner,
         )
     }
 
+    /// Presign an operation for stat(head) with additional options.
+    ///
+    /// # Options
+    ///
+    /// Visit [`options::StatOptions`] for all available options.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use anyhow::Result;
+    /// use opendal::Operator;
+    /// use opendal::options;
+    /// use std::time::Duration;
+    ///
+    /// async fn test(op: Operator) -> Result<()> {
+    ///     let signed_req = op.presign_stat_options(
+    ///         "test",
+    ///         Duration::from_secs(3600),
+    ///         options::StatOptions {
+    ///             if_match: Some("<etag>".to_string()),
+    ///             ..Default::default()
+    ///         }
+    ///     ).await?;
+    ///     let req = http::Request::builder()
+    ///         .method(signed_req.method())
+    ///         .uri(signed_req.uri())
+    ///         .body(())?;
+    ///
+    /// #    Ok(())
+    /// # }
+    /// ```
+    pub async fn presign_stat_options(
+        &self,
+        path: &str,
+        expire: Duration,
+        opts: options::StatOptions,
+    ) -> Result<PresignedRequest> {
+        let path = normalize_path(path);
+        Self::presign_stat_inner(self.inner().clone(), path, (opts, 
expire)).await
+    }
+
+    #[inline]
+    async fn presign_stat_inner(
+        acc: Accessor,
+        path: String,
+        (opts, expire): (options::StatOptions, Duration),
+    ) -> Result<PresignedRequest> {
+        let op = OpPresign::new(OpStat::from(opts), expire);
+        let rp = acc.presign(&path, op).await?;
+        Ok(rp.into_presigned_request())
+    }
+
     /// Presign an operation for read.
     ///
     /// # Notes
@@ -1668,8 +1716,8 @@ impl Operator {
     /// ## Extra Options
     ///
     /// `presign_read` is a wrapper of [`Self::presign_read_with`] without any 
options. To use
-    /// extra options like `override_content_disposition`, please use 
[`Self::presign_read_with`]
-    /// instead.
+    /// extra options like `override_content_disposition`, please use 
[`Self::presign_read_with`] or
+    /// [`Self::presign_read_options] instead.
     ///
     /// # Example
     ///
@@ -1707,80 +1755,91 @@ impl Operator {
     ///
     /// # Options
     ///
-    /// ## `override_content_disposition`
+    /// Visit [`options::ReadOptions`] for all available options.
     ///
-    /// Override the 
[`content-disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition)
 header returned by storage services.
+    /// # Example
     ///
     /// ```
     /// use std::time::Duration;
     ///
     /// use anyhow::Result;
+    /// use futures::io;
     /// use opendal::Operator;
     ///
     /// async fn test(op: Operator) -> Result<()> {
     ///     let signed_req = op
     ///         .presign_read_with("test.txt", Duration::from_secs(3600))
-    ///         .override_content_disposition("attachment; 
filename=\"othertext.txt\"")
+    ///         .override_content_type("text/plain")
     ///         .await?;
     ///     Ok(())
     /// }
     /// ```
+    pub fn presign_read_with(
+        &self,
+        path: &str,
+        expire: Duration,
+    ) -> FuturePresignRead<impl Future<Output = Result<PresignedRequest>>> {
+        let path = normalize_path(path);
+
+        OperatorFuture::new(
+            self.inner().clone(),
+            path,
+            (options::ReadOptions::default(), expire),
+            Self::presign_read_inner,
+        )
+    }
+
+    /// Presign an operation for read with additional options.
     ///
-    /// ## `override_cache_control`
-    ///
-    /// Override the 
[`cache-control`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control)
 header returned by storage services.
-    ///
-    /// ```
-    /// use std::time::Duration;
-    ///
-    /// use anyhow::Result;
-    /// use opendal::Operator;
-    ///
-    /// async fn test(op: Operator) -> Result<()> {
-    ///     let signed_req = op
-    ///         .presign_read_with("test.txt", Duration::from_secs(3600))
-    ///         .override_cache_control("no-store")
-    ///         .await?;
-    ///     Ok(())
-    /// }
-    /// ```
+    /// # Options
     ///
-    /// ## `override_content_type`
+    /// Visit [`options::ReadOptions`] for all available options.
     ///
-    /// Override the 
[`content-type`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type)
 header returned by storage services.
+    /// # Example
     ///
     /// ```
-    /// use std::time::Duration;
-    ///
     /// use anyhow::Result;
-    /// use futures::io;
     /// use opendal::Operator;
+    /// use opendal::options;
+    /// use std::time::Duration;
     ///
     /// async fn test(op: Operator) -> Result<()> {
-    ///     let signed_req = op
-    ///         .presign_read_with("test.txt", Duration::from_secs(3600))
-    ///         .override_content_type("text/plain")
-    ///         .await?;
-    ///     Ok(())
-    /// }
+    ///     let signed_req = op.presign_read_options(
+    ///         "file",
+    ///         Duration::from_secs(3600),
+    ///         options::ReadOptions {
+    ///             override_content_disposition: Some("attachment; 
filename=\"othertext.txt\"".to_string()),
+    ///             ..Default::default()
+    ///         }
+    ///     ).await?;
+    ///     let req = http::Request::builder()
+    ///         .method(signed_req.method())
+    ///         .uri(signed_req.uri())
+    ///         .body(())?;
+    ///
+    /// #    Ok(())
+    /// # }
     /// ```
-    pub fn presign_read_with(
+    pub async fn presign_read_options(
         &self,
         path: &str,
         expire: Duration,
-    ) -> FuturePresignRead<impl Future<Output = Result<PresignedRequest>>> {
+        opts: options::ReadOptions,
+    ) -> Result<PresignedRequest> {
         let path = normalize_path(path);
+        Self::presign_read_inner(self.inner().clone(), path, (opts, 
expire)).await
+    }
 
-        OperatorFuture::new(
-            self.inner().clone(),
-            path,
-            (OpRead::default(), expire),
-            |inner, path, (args, dur)| async move {
-                let op = OpPresign::new(args, dur);
-                let rp = inner.presign(&path, op).await?;
-                Ok(rp.into_presigned_request())
-            },
-        )
+    #[inline]
+    async fn presign_read_inner(
+        acc: Accessor,
+        path: String,
+        (opts, expire): (options::ReadOptions, Duration),
+    ) -> Result<PresignedRequest> {
+        let (op_read, _) = opts.into();
+        let op = OpPresign::new(op_read, expire);
+        let rp = acc.presign(&path, op).await?;
+        Ok(rp.into_presigned_request())
     }
 
     /// Presign an operation for write.
@@ -1790,7 +1849,8 @@ impl Operator {
     /// ## Extra Options
     ///
     /// `presign_write` is a wrapper of [`Self::presign_write_with`] without 
any options. To use
-    /// extra options like `content_type`, please use 
[`Self::presign_write_with`] instead.
+    /// extra options like `content_type`, please use 
[`Self::presign_write_with`] or
+    /// [`Self::presign_write_options`] instead.
     ///
     /// # Example
     ///
@@ -1825,9 +1885,9 @@ impl Operator {
     ///
     /// # Options
     ///
-    /// ## `content_type`
+    /// Visit [`options::WriteOptions`] for all available options.    
     ///
-    /// Set the 
[`content-type`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type)
 header returned by storage services.
+    /// # Example
     ///
     /// ```
     /// use std::time::Duration;
@@ -1838,7 +1898,7 @@ impl Operator {
     /// async fn test(op: Operator) -> Result<()> {
     ///     let signed_req = op
     ///         .presign_write_with("test", Duration::from_secs(3600))
-    ///         .content_type("text/csv")
+    ///         .cache_control("no-store")
     ///         .await?;
     ///     let req = http::Request::builder()
     ///         .method(signed_req.method())
@@ -1848,71 +1908,74 @@ impl Operator {
     ///     Ok(())
     /// }
     /// ```
+    pub fn presign_write_with(
+        &self,
+        path: &str,
+        expire: Duration,
+    ) -> FuturePresignWrite<impl Future<Output = Result<PresignedRequest>>> {
+        let path = normalize_path(path);
+
+        OperatorFuture::new(
+            self.inner().clone(),
+            path,
+            (options::WriteOptions::default(), expire),
+            Self::presign_write_inner,
+        )
+    }
+
+    /// Presign an operation for write with additional options.
     ///
-    /// ## `content_disposition`
-    ///
-    /// Set the 
[`content-disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition)
 header returned by storage services.
-    ///
-    /// ```
-    /// use std::time::Duration;
-    ///
-    /// use anyhow::Result;
-    /// use opendal::Operator;
-    ///
-    /// async fn test(op: Operator) -> Result<()> {
-    ///     let signed_req = op
-    ///         .presign_write_with("test", Duration::from_secs(3600))
-    ///         .content_disposition("attachment; filename=\"cool.html\"")
-    ///         .await?;
-    ///     let req = http::Request::builder()
-    ///         .method(signed_req.method())
-    ///         .uri(signed_req.uri())
-    ///         .body(())?;
-    ///
-    ///     Ok(())
-    /// }
-    /// ```
+    /// # Options
     ///
-    /// ## `cache_control`
+    /// Check [`options::WriteOptions`] for all available options.
     ///
-    /// Set the 
[`cache-control`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control)
 header returned by storage services.
+    /// # Example
     ///
     /// ```
-    /// use std::time::Duration;
-    ///
     /// use anyhow::Result;
     /// use opendal::Operator;
+    /// use opendal::options;
+    /// use std::time::Duration;
     ///
     /// async fn test(op: Operator) -> Result<()> {
-    ///     let signed_req = op
-    ///         .presign_write_with("test", Duration::from_secs(3600))
-    ///         .cache_control("no-store")
-    ///         .await?;
+    ///     let signed_req = op.presign_write_options(
+    ///         "file",
+    ///         Duration::from_secs(3600),
+    ///         options::WriteOptions {
+    ///             content_type: Some("application/json".to_string()),
+    ///             cache_control: Some("max-age=3600".to_string()),
+    ///             if_not_exists: true,
+    ///             ..Default::default()
+    ///         }
+    ///     ).await?;
     ///     let req = http::Request::builder()
     ///         .method(signed_req.method())
     ///         .uri(signed_req.uri())
     ///         .body(())?;
     ///
-    ///     Ok(())
-    /// }
+    /// #    Ok(())
+    /// # }
     /// ```
-    pub fn presign_write_with(
+    pub async fn presign_write_options(
         &self,
         path: &str,
         expire: Duration,
-    ) -> FuturePresignWrite<impl Future<Output = Result<PresignedRequest>>> {
+        opts: options::WriteOptions,
+    ) -> Result<PresignedRequest> {
         let path = normalize_path(path);
+        Self::presign_write_inner(self.inner().clone(), path, (opts, 
expire)).await
+    }
 
-        OperatorFuture::new(
-            self.inner().clone(),
-            path,
-            (OpWrite::default(), expire),
-            |inner, path, (args, dur)| async move {
-                let op = OpPresign::new(args, dur);
-                let rp = inner.presign(&path, op).await?;
-                Ok(rp.into_presigned_request())
-            },
-        )
+    #[inline]
+    async fn presign_write_inner(
+        acc: Accessor,
+        path: String,
+        (opts, expire): (options::WriteOptions, Duration),
+    ) -> Result<PresignedRequest> {
+        let (op_write, _) = opts.into();
+        let op = OpPresign::new(op_write, expire);
+        let rp = acc.presign(&path, op).await?;
+        Ok(rp.into_presigned_request())
     }
 
     /// Presign an operation for delete.
@@ -1963,12 +2026,59 @@ impl Operator {
         OperatorFuture::new(
             self.inner().clone(),
             path,
-            (OpDelete::default(), expire),
-            |inner, path, (args, dur)| async move {
-                let op = OpPresign::new(args, dur);
-                let rp = inner.presign(&path, op).await?;
-                Ok(rp.into_presigned_request())
-            },
+            (options::DeleteOptions::default(), expire),
+            Self::presign_delete_inner,
         )
     }
+
+    /// Presign an operation for delete with additional options.
+    ///
+    /// # Options
+    ///
+    /// Visit [`options::DeleteOptions`] for all available options.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use anyhow::Result;
+    /// use opendal::Operator;
+    /// use opendal::options;
+    /// use std::time::Duration;
+    ///
+    /// async fn test(op: Operator) -> Result<()> {
+    ///     let signed_req = op.presign_delete_options(
+    ///         "path/to/file",
+    ///         Duration::from_secs(3600),
+    ///         options::DeleteOptions {
+    ///             ..Default::default()
+    ///         }
+    ///     ).await?;
+    ///     let req = http::Request::builder()
+    ///         .method(signed_req.method())
+    ///         .uri(signed_req.uri())
+    ///         .body(())?;
+    ///
+    /// #    Ok(())
+    /// # }
+    /// ```
+    pub async fn presign_delete_options(
+        &self,
+        path: &str,
+        expire: Duration,
+        opts: options::DeleteOptions,
+    ) -> Result<PresignedRequest> {
+        let path = normalize_path(path);
+        Self::presign_delete_inner(self.inner().clone(), path, (opts, 
expire)).await
+    }
+
+    #[inline]
+    async fn presign_delete_inner(
+        acc: Accessor,
+        path: String,
+        (opts, expire): (options::DeleteOptions, Duration),
+    ) -> Result<PresignedRequest> {
+        let op = OpPresign::new(OpDelete::from(opts), expire);
+        let rp = acc.presign(&path, op).await?;
+        Ok(rp.into_presigned_request())
+    }
 }
diff --git a/core/src/types/operator/operator_futures.rs 
b/core/src/types/operator/operator_futures.rs
index 056d6e7ab..ef263fd24 100644
--- a/core/src/types/operator/operator_futures.rs
+++ b/core/src/types/operator/operator_futures.rs
@@ -70,14 +70,6 @@ impl<I, O, F: Future<Output = Result<O>>> OperatorFuture<I, 
O, F> {
     }
 }
 
-impl<I, O, F: Future<Output = Result<O>>> OperatorFuture<I, O, F> {
-    /// Change the operation's args.
-    pub(crate) fn map(mut self, f: impl FnOnce(I) -> I) -> Self {
-        self.args = f(self.args);
-        self
-    }
-}
-
 impl<I, O, F> IntoFuture for OperatorFuture<I, O, F>
 where
     F: Future<Output = Result<O>>,
@@ -140,98 +132,116 @@ impl<F: Future<Output = Result<Metadata>>> FutureStat<F> 
{
 /// Future that generated by [`Operator::presign_stat_with`].
 ///
 /// Users can add more options by public functions provided by this struct.
-pub type FuturePresignStat<F> = OperatorFuture<(OpStat, Duration), 
PresignedRequest, F>;
+pub type FuturePresignStat<F> =
+    OperatorFuture<(options::StatOptions, Duration), PresignedRequest, F>;
 
 impl<F: Future<Output = Result<PresignedRequest>>> FuturePresignStat<F> {
-    /// Sets the content-disposition header that should be sent back by the 
remote read operation.
-    pub fn override_content_disposition(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_override_content_disposition(v), 
dur))
+    /// Refer to [`options::StatOptions::override_content_disposition`] for 
more details.
+    pub fn override_content_disposition(mut self, v: &str) -> Self {
+        self.args.0.override_content_disposition = Some(v.to_string());
+        self
     }
 
-    /// Sets the cache-control header that should be sent back by the remote 
read operation.
-    pub fn override_cache_control(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_override_cache_control(v), dur))
+    /// Refer to [`options::StatOptions::override_cache_control`] for more 
details.
+    pub fn override_cache_control(mut self, v: &str) -> Self {
+        self.args.0.override_cache_control = Some(v.to_string());
+        self
     }
 
-    /// Sets the content-type header that should be sent back by the remote 
read operation.
-    pub fn override_content_type(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_override_content_type(v), dur))
+    /// Refer to [`options::StatOptions::override_content_type`] for more 
details.
+    pub fn override_content_type(mut self, v: &str) -> Self {
+        self.args.0.override_content_type = Some(v.to_string());
+        self
     }
 
-    /// Set the If-Match of the option
-    pub fn if_match(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_if_match(v), dur))
+    /// Refer to [`options::StatOptions::if_match`] for more details.
+    pub fn if_match(mut self, v: &str) -> Self {
+        self.args.0.if_match = Some(v.to_string());
+        self
     }
 
-    /// Set the If-None-Match of the option
-    pub fn if_none_match(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_if_none_match(v), dur))
+    /// Refer to [`options::StatOptions::if_none_match`] for more details.
+    pub fn if_none_match(mut self, v: &str) -> Self {
+        self.args.0.if_none_match = Some(v.to_string());
+        self
     }
 }
 
 /// Future that generated by [`Operator::presign_delete_with`].
 ///
 /// Users can add more options by public functions provided by this struct.
-pub type FuturePresignDelete<F> = OperatorFuture<(OpDelete, Duration), 
PresignedRequest, F>;
+pub type FuturePresignDelete<F> =
+    OperatorFuture<(options::DeleteOptions, Duration), PresignedRequest, F>;
 
 impl<F: Future<Output = Result<PresignedRequest>>> FuturePresignDelete<F> {}
 
 /// Future that generated by [`Operator::presign_read_with`].
 ///
 /// Users can add more options by public functions provided by this struct.
-pub type FuturePresignRead<F> = OperatorFuture<(OpRead, Duration), 
PresignedRequest, F>;
+pub type FuturePresignRead<F> =
+    OperatorFuture<(options::ReadOptions, Duration), PresignedRequest, F>;
 
 impl<F: Future<Output = Result<PresignedRequest>>> FuturePresignRead<F> {
-    /// Sets the content-disposition header that should be sent back by the 
remote read operation.
-    pub fn override_content_disposition(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_override_content_disposition(v), 
dur))
+    /// Refer to [`options::ReadOptions::override_content_disposition`] for 
more details.
+    pub fn override_content_disposition(mut self, v: &str) -> Self {
+        self.args.0.override_content_disposition = Some(v.to_string());
+        self
     }
 
-    /// Sets the cache-control header that should be sent back by the remote 
read operation.
-    pub fn override_cache_control(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_override_cache_control(v), dur))
+    /// Refer to [`options::ReadOptions::override_cache_control`] for more 
details.
+    pub fn override_cache_control(mut self, v: &str) -> Self {
+        self.args.0.override_cache_control = Some(v.to_string());
+        self
     }
 
-    /// Sets the content-type header that should be sent back by the remote 
read operation.
-    pub fn override_content_type(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_override_content_type(v), dur))
+    /// Refer to [`options::ReadOptions::override_content_type`] for more 
details.
+    pub fn override_content_type(mut self, v: &str) -> Self {
+        self.args.0.override_content_type = Some(v.to_string());
+        self
     }
 
-    /// Set the If-Match of the option
-    pub fn if_match(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_if_match(v), dur))
+    /// Refer to [`options::ReadOptions::if_match`] for more details.
+    pub fn if_match(mut self, v: &str) -> Self {
+        self.args.0.if_match = Some(v.to_string());
+        self
     }
 
-    /// Set the If-None-Match of the option
-    pub fn if_none_match(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_if_none_match(v), dur))
+    /// Refer to [`options::ReadOptions::if_none_match`] for more details.
+    pub fn if_none_match(mut self, v: &str) -> Self {
+        self.args.0.if_none_match = Some(v.to_string());
+        self
     }
 }
 
 /// Future that generated by [`Operator::presign_write_with`].
 ///
 /// Users can add more options by public functions provided by this struct.
-pub type FuturePresignWrite<F> = OperatorFuture<(OpWrite, Duration), 
PresignedRequest, F>;
+pub type FuturePresignWrite<F> =
+    OperatorFuture<(options::WriteOptions, Duration), PresignedRequest, F>;
 
 impl<F: Future<Output = Result<PresignedRequest>>> FuturePresignWrite<F> {
-    /// Set the content type of option
-    pub fn content_type(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_content_type(v), dur))
+    /// Refer to [`options::WriteOptions::content_type`] for more details.
+    pub fn content_type(mut self, v: &str) -> Self {
+        self.args.0.content_type = Some(v.to_string());
+        self
     }
 
-    /// Set the content disposition of option
-    pub fn content_disposition(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_content_disposition(v), dur))
+    /// Refer to [`options::WriteOptions::content_disposition`] for more 
details.
+    pub fn content_disposition(mut self, v: &str) -> Self {
+        self.args.0.content_disposition = Some(v.to_string());
+        self
     }
 
-    /// Set the content encoding of the operation
-    pub fn content_encoding(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_content_encoding(v), dur))
+    /// Refer to [`options::WriteOptions::content_encoding`] for more details.
+    pub fn content_encoding(mut self, v: &str) -> Self {
+        self.args.0.content_encoding = Some(v.to_string());
+        self
     }
 
-    /// Set the content type of option
-    pub fn cache_control(self, v: &str) -> Self {
-        self.map(|(args, dur)| (args.with_cache_control(v), dur))
+    /// Refer to [`options::WriteOptions::cache_control`] for more details.
+    pub fn cache_control(mut self, v: &str) -> Self {
+        self.args.0.cache_control = Some(v.to_string());
+        self
     }
 }
 

Reply via email to