// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! Futures provides the futures generated by [`Operator`]
//!
//! By using futures, users can add more options for operation.

use std::collections::HashMap;
use std::future::IntoFuture;

use crate::raw::*;
use crate::*;
use futures::Future;

/// [`Operator`] produces this future.
///
/// The future consumes its inputs when it begins executing.
///
/// # NOTES
///
/// OpenDAL uses this type as an implementation detail. Users should not use it directly.
pub struct OperatorFuture<I, O, F: Future<Output = Result<O>>> {
    /// The composed context used to execute operations.
    ctx: OperationContext,
    /// The composed service to the underlying object storage.
    srv: Servicer,
    /// The path of string
    path: String,
    /// The input args
    args: I,
    /// The function which will move all the args and return a static future
    f: fn(OperationContext, Servicer, String, I) -> F,
}

impl<I, O, F: Future<Output = Result<O>>> OperatorFuture<I, O, F> {
    /// # NOTES
    ///
    /// OpenDAL uses this type as an implementation detail. Users should not use it directly.
    pub(crate) fn new(
        ctx: OperationContext,
        srv: Servicer,
        path: String,
        args: I,
        f: fn(OperationContext, Servicer, String, I) -> F,
    ) -> Self {
        OperatorFuture {
            ctx,
            srv,
            path,
            args,
            f,
        }
    }
}

impl<I, O, F> IntoFuture for OperatorFuture<I, O, F>
where
    F: Future<Output = Result<O>>,
{
    type Output = Result<O>;
    type IntoFuture = F;

    fn into_future(self) -> Self::IntoFuture {
        (self.f)(self.ctx, self.srv, self.path, self.args)
    }
}

/// [`Operator::stat_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FutureStat<F> = OperatorFuture<options::StatOptions, Metadata, F>;

impl<F: Future<Output = Result<Metadata>>> FutureStat<F> {
    /// Stat only when the file has this exact ETag.
    ///
    /// Refer to [`options::StatOptions::if_match`] for the full contract.
    pub fn if_match(mut self, v: &str) -> Self {
        self.args.if_match = Some(v.to_string());
        self
    }

    /// Stat only when the file does not have this ETag.
    ///
    /// Refer to [`options::StatOptions::if_none_match`] for the full contract.
    pub fn if_none_match(mut self, v: &str) -> Self {
        self.args.if_none_match = Some(v.to_string());
        self
    }

    /// Stat only when the file has this exact version.
    ///
    /// Refer to [`options::StatOptions::if_version_match`] for the full contract.
    pub fn if_version_match(mut self, v: &str) -> Self {
        self.args.if_version_match = Some(v.to_string());
        self
    }

    /// Stat only when the file does not have this version.
    ///
    /// Refer to [`options::StatOptions::if_version_not_match`] for the full contract.
    pub fn if_version_not_match(mut self, v: &str) -> Self {
        self.args.if_version_not_match = Some(v.to_string());
        self
    }

    /// Stat only when the file was modified after this timestamp.
    ///
    /// Refer to [`options::StatOptions::if_modified_since`] for the full contract.
    pub fn if_modified_since(mut self, v: Timestamp) -> Self {
        self.args.if_modified_since = Some(v);
        self
    }

    /// Stat only when the file was not modified after this timestamp.
    ///
    /// Refer to [`options::StatOptions::if_unmodified_since`] for the full contract.
    pub fn if_unmodified_since(mut self, v: Timestamp) -> Self {
        self.args.if_unmodified_since = Some(v);
        self
    }

    /// Stat the given version of the file instead of the current one.
    ///
    /// Refer to [`options::StatOptions::version`] for the full contract.
    pub fn version(mut self, v: &str) -> Self {
        self.args.version = Some(v.to_string());
        self
    }
}

/// [`Operator::presign_stat_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FuturePresignStat<F> =
    OperatorFuture<(options::StatOptions, Duration), PresignedRequest, F>;

impl<F: Future<Output = Result<PresignedRequest>>> FuturePresignStat<F> {
    /// 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
    }

    /// 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
    }

    /// 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
    }

    /// Make the presigned request stat only when the file has this exact ETag.
    ///
    /// Refer to [`options::StatOptions::if_match`] for the full contract.
    pub fn if_match(mut self, v: &str) -> Self {
        self.args.0.if_match = Some(v.to_string());
        self
    }

    /// Make the presigned request stat only when the file does not have this ETag.
    ///
    /// Refer to [`options::StatOptions::if_none_match`] for the full contract.
    pub fn if_none_match(mut self, v: &str) -> Self {
        self.args.0.if_none_match = Some(v.to_string());
        self
    }
}

/// [`Operator::presign_delete_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FuturePresignDelete<F> =
    OperatorFuture<(options::DeleteOptions, Duration), PresignedRequest, F>;

impl<F: Future<Output = Result<PresignedRequest>>> FuturePresignDelete<F> {}

/// [`Operator::presign_read_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FuturePresignRead<F> =
    OperatorFuture<(options::ReadOptions, Duration), PresignedRequest, F>;

impl<F: Future<Output = Result<PresignedRequest>>> FuturePresignRead<F> {
    /// 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
    }

    /// 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
    }

    /// 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
    }

    /// Make the presigned request read only when the file has this exact ETag.
    ///
    /// Refer to [`options::ReadOptions::if_match`] for the full contract.
    pub fn if_match(mut self, v: &str) -> Self {
        self.args.0.if_match = Some(v.to_string());
        self
    }

    /// Make the presigned request read only when the file does not have this ETag.
    ///
    /// Refer to [`options::ReadOptions::if_none_match`] for the full contract.
    pub fn if_none_match(mut self, v: &str) -> Self {
        self.args.0.if_none_match = Some(v.to_string());
        self
    }
}

/// [`Operator::presign_write_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FuturePresignWrite<F> =
    OperatorFuture<(options::WriteOptions, Duration), PresignedRequest, F>;

impl<F: Future<Output = Result<PresignedRequest>>> FuturePresignWrite<F> {
    /// 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
    }

    /// 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
    }

    /// 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
    }

    /// 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
    }
}

/// [`Operator::read_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FutureRead<F> = OperatorFuture<options::ReadOptions, Buffer, F>;

impl<F: Future<Output = Result<Buffer>>> FutureRead<F> {
    /// Set `range` for this `read` request.
    ///
    /// If we have a file with size `n`.
    ///
    /// - `..` means read bytes in range `[0, n)` of file.
    /// - `0..1024` and `..1024` means read bytes in range `[0, 1024)` of file
    /// - `1024..` means read bytes in range `[1024, n)` of file
    /// - `BytesRange::suffix(1024)` means read the last `min(1024, n)` bytes of file
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::TryStreamExt;
    /// # async fn test(op: Operator) -> Result<()> {
    /// let bs = op.read_with("path/to/file").range(0..1024).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn range(mut self, range: impl Into<BytesRange>) -> Self {
        self.args.range = range.into();
        self
    }

    /// Sets known content length as a hint for this `read` request.
    ///
    /// Refer to [`options::ReadOptions::content_length_hint`] for more details.
    pub fn content_length_hint(mut self, v: u64) -> Self {
        self.args.content_length_hint = Some(v);
        self
    }

    /// Set `concurrent` for the reader.
    ///
    /// OpenDAL by default to write file without concurrent. This is not efficient for cases when users
    /// read large chunks of data. By setting `concurrent`, opendal will read files concurrently
    /// on support storage services.
    ///
    /// By setting `concurrent`, opendal will fetch chunks concurrently with
    /// the given chunk size.
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # async fn test(op: Operator) -> Result<()> {
    /// let r = op.read_with("path/to/file").concurrent(8).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn concurrent(mut self, concurrent: usize) -> Self {
        self.args.concurrent = concurrent.max(1);
        self
    }

    /// OpenDAL will use services' preferred chunk size by default. Users can set chunk based on their own needs.
    ///
    /// This following example will make opendal read data in 4MiB chunks:
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # async fn test(op: Operator) -> Result<()> {
    /// let r = op.read_with("path/to/file").chunk(4 * 1024 * 1024).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn chunk(mut self, chunk_size: usize) -> Self {
        self.args.chunk = Some(chunk_size);
        self
    }

    /// Read the given version of the file instead of the current one.
    ///
    /// Refer to [`options::ReadOptions::version`] for the full contract.
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    ///
    /// # async fn test(op: Operator, version: &str) -> Result<()> {
    /// let mut bs = op.read_with("path/to/file").version(version).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn version(mut self, v: &str) -> Self {
        self.args.version = Some(v.to_string());
        self
    }

    /// Read only when the file has this exact ETag.
    ///
    /// Refer to [`options::ReadOptions::if_match`] for the full contract.
    ///
    /// ```
    /// # use opendal_core::Result;
    /// use opendal_core::Operator;
    /// # async fn test(op: Operator, etag: &str) -> Result<()> {
    /// let mut metadata = op.read_with("path/to/file").if_match(etag).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_match(mut self, v: &str) -> Self {
        self.args.if_match = Some(v.to_string());
        self
    }

    /// Read only when the file does not have this ETag.
    ///
    /// Refer to [`options::ReadOptions::if_none_match`] for the full contract.
    ///
    /// ```
    /// # use opendal_core::Result;
    /// use opendal_core::Operator;
    /// # async fn test(op: Operator, etag: &str) -> Result<()> {
    /// let mut metadata = op.read_with("path/to/file").if_none_match(etag).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_none_match(mut self, v: &str) -> Self {
        self.args.if_none_match = Some(v.to_string());
        self
    }

    /// Read only when the file has this exact version.
    ///
    /// Refer to [`options::ReadOptions::if_version_match`] for the full contract.
    pub fn if_version_match(mut self, v: &str) -> Self {
        self.args.if_version_match = Some(v.to_string());
        self
    }

    /// Read only when the file does not have this version.
    ///
    /// Refer to [`options::ReadOptions::if_version_not_match`] for the full contract.
    pub fn if_version_not_match(mut self, v: &str) -> Self {
        self.args.if_version_not_match = Some(v.to_string());
        self
    }

    /// Read only when the file was modified after this timestamp.
    ///
    /// Refer to [`options::ReadOptions::if_modified_since`] for the full contract.
    ///
    /// ```
    /// # use opendal_core::Result;
    /// use jiff::Timestamp;
    /// use opendal_core::Operator;
    /// # async fn test(op: Operator, time: Timestamp) -> Result<()> {
    /// let mut metadata = op.read_with("path/to/file").if_modified_since(time).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_modified_since(mut self, v: impl Into<Timestamp>) -> Self {
        self.args.if_modified_since = Some(v.into());
        self
    }

    /// Read only when the file was not modified after this timestamp.
    ///
    /// Refer to [`options::ReadOptions::if_unmodified_since`] for the full contract.
    ///
    /// ```
    /// # use opendal_core::Result;
    /// use jiff::Timestamp;
    /// use opendal_core::Operator;
    /// # async fn test(op: Operator, time: Timestamp) -> Result<()> {
    /// let mut metadata = op
    ///     .read_with("path/to/file")
    ///     .if_unmodified_since(time)
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_unmodified_since(mut self, v: impl Into<Timestamp>) -> Self {
        self.args.if_unmodified_since = Some(v.into());
        self
    }
}

/// [`Operator::read_with`] and [`Operator::reader_with`] return this future.
///
/// Use its methods to configure the operation before awaiting it.
///
/// # Notes
///
/// `(OpRead, ())` is a trick to make sure `FutureReader` is different from `FutureRead`
pub type FutureReader<F> = OperatorFuture<options::ReaderOptions, Reader, F>;

impl<F: Future<Output = Result<Reader>>> FutureReader<F> {
    /// Read the given version of the file instead of the current one.
    ///
    /// Refer to [`options::ReaderOptions::version`] for the full contract.
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    ///
    /// # async fn test(op: Operator, version: &str) -> Result<()> {
    /// let mut r = op.reader_with("path/to/file").version(version).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn version(mut self, v: &str) -> Self {
        self.args.version = Some(v.to_string());
        self
    }

    /// Set `concurrent` for the reader.
    ///
    /// OpenDAL by default to write file without concurrent. This is not efficient for cases when users
    /// read large chunks of data. By setting `concurrent`, opendal will reading files concurrently
    /// on support storage services.
    ///
    /// By setting `concurrent`, opendal will fetch chunks concurrently with
    /// the give chunk size.
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # async fn test(op: Operator) -> Result<()> {
    /// let r = op.reader_with("path/to/file").concurrent(8).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn concurrent(mut self, concurrent: usize) -> Self {
        self.args.concurrent = concurrent.max(1);
        self
    }

    /// OpenDAL will use services' preferred chunk size by default. Users can set chunk based on their own needs.
    ///
    /// This following example will make opendal read data in 4MiB chunks:
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # async fn test(op: Operator) -> Result<()> {
    /// let r = op
    ///     .reader_with("path/to/file")
    ///     .chunk(4 * 1024 * 1024)
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn chunk(mut self, chunk_size: usize) -> Self {
        self.args.chunk = Some(chunk_size);
        self
    }

    /// Sets known content length as a hint for this `reader`.
    ///
    /// Refer to [`options::ReaderOptions::content_length_hint`] for more details.
    pub fn content_length_hint(mut self, v: u64) -> Self {
        self.args.content_length_hint = Some(v);
        self
    }

    /// Controls the optimization strategy for range reads in [`Reader::fetch`].
    ///
    /// When performing range reads, if the gap between two requested ranges is less than or
    /// equal to the configured `gap` size, OpenDAL will merge these ranges into a single read request
    /// and discard the unrequested data in between. This helps reduce the number of API calls
    /// to remote storage services.
    ///
    /// This optimization is particularly useful when performing multiple small range reads
    /// that are close to each other, as it reduces the overhead of multiple network requests
    /// at the cost of transferring some additional data.
    ///
    /// Set to `0` to disable merging ranges separated by a gap. Overlapping or adjacent ranges
    /// are still merged.
    ///
    /// In this example, if two requested ranges are separated by no more than 1 MiB,
    /// they will be merged into a single read request:
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # async fn test(op: Operator) -> Result<()> {
    /// let r = op
    ///     .reader_with("path/to/file")
    ///     .chunk(4 * 1024 * 1024)
    ///     .gap(1024 * 1024) // 1MiB gap
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn gap(mut self, gap_size: usize) -> Self {
        self.args.gap = Some(gap_size);
        self
    }

    /// Read only when the file has this exact ETag.
    ///
    /// Refer to [`options::ReaderOptions::if_match`] for the full contract.
    ///
    /// ```
    /// # use opendal_core::Result;
    /// use opendal_core::Operator;
    /// # async fn test(op: Operator, etag: &str) -> Result<()> {
    /// let mut r = op.reader_with("path/to/file").if_match(etag).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_match(mut self, etag: &str) -> Self {
        self.args.if_match = Some(etag.to_string());
        self
    }

    /// Read only when the file does not have this ETag.
    ///
    /// Refer to [`options::ReaderOptions::if_none_match`] for the full contract.
    ///
    /// ```
    /// # use opendal_core::Result;
    /// use opendal_core::Operator;
    /// # async fn test(op: Operator, etag: &str) -> Result<()> {
    /// let mut r = op.reader_with("path/to/file").if_none_match(etag).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_none_match(mut self, etag: &str) -> Self {
        self.args.if_none_match = Some(etag.to_string());
        self
    }

    /// Read only when the file has this exact version.
    ///
    /// Refer to [`options::ReaderOptions::if_version_match`] for the full contract.
    pub fn if_version_match(mut self, version: &str) -> Self {
        self.args.if_version_match = Some(version.to_string());
        self
    }

    /// Read only when the file does not have this version.
    ///
    /// Refer to [`options::ReaderOptions::if_version_not_match`] for the full contract.
    pub fn if_version_not_match(mut self, version: &str) -> Self {
        self.args.if_version_not_match = Some(version.to_string());
        self
    }

    /// Read only when the file was modified after this timestamp.
    ///
    /// Refer to [`options::ReaderOptions::if_modified_since`] for the full contract.
    ///
    /// ```
    /// # use opendal_core::Result;
    /// use jiff::Timestamp;
    /// use opendal_core::Operator;
    /// # async fn test(op: Operator, time: Timestamp) -> Result<()> {
    /// let mut r = op
    ///     .reader_with("path/to/file")
    ///     .if_modified_since(time)
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_modified_since(mut self, v: impl Into<Timestamp>) -> Self {
        self.args.if_modified_since = Some(v.into());
        self
    }

    /// Read only when the file was not modified after this timestamp.
    ///
    /// Refer to [`options::ReaderOptions::if_unmodified_since`] for the full contract.
    ///
    /// ```
    /// # use opendal_core::Result;
    /// use jiff::Timestamp;
    /// use opendal_core::Operator;
    /// # async fn test(op: Operator, time: Timestamp) -> Result<()> {
    /// let mut r = op
    ///     .reader_with("path/to/file")
    ///     .if_unmodified_since(time)
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_unmodified_since(mut self, v: impl Into<Timestamp>) -> Self {
        self.args.if_unmodified_since = Some(v.into());
        self
    }
}

/// [`Operator::write_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FutureWrite<F> = OperatorFuture<(options::WriteOptions, Buffer), Metadata, F>;

impl<F: Future<Output = Result<Metadata>>> FutureWrite<F> {
    /// Sets append mode for this write request.
    ///
    /// Refer to [`options::WriteOptions::append`] for more details.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let _ = op
    ///     .write_with("path/to/file", vec![0; 4096])
    ///     .append(true)
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn append(mut self, v: bool) -> Self {
        self.args.0.append = v;
        self
    }

    /// Sets chunk size for buffered writes.
    ///
    /// Refer to [`options::WriteOptions::chunk`] for more details.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// // Set 8MiB chunk size - data will be sent in one API call at close
    /// let _ = op
    ///     .write_with("path/to/file", vec![0; 4096])
    ///     .chunk(8 * 1024 * 1024)
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn chunk(mut self, v: usize) -> Self {
        self.args.0.chunk = Some(v);
        self
    }

    /// Sets concurrent write operations for this writer.
    ///
    /// Refer to [`options::WriteOptions::concurrent`] for more details.
    ///
    /// ## Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// // Enable concurrent writes with 8 parallel operations at 128B chunk.
    /// let _ = op
    ///     .write_with("path/to/file", vec![0; 4096])
    ///     .chunk(128)
    ///     .concurrent(8)
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn concurrent(mut self, v: usize) -> Self {
        self.args.0.concurrent = v.max(1);
        self
    }

    /// Sets Cache-Control header for this write operation.
    ///
    /// Refer to [`options::WriteOptions::cache_control`] for more details.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// // Cache content for 7 days (604800 seconds)
    /// let _ = op
    ///     .write_with("path/to/file", vec![0; 4096])
    ///     .cache_control("max-age=604800")
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn cache_control(mut self, v: &str) -> Self {
        self.args.0.cache_control = Some(v.to_string());
        self
    }

    /// Sets `Content-Type` header for this write operation.
    ///
    /// Refer to [`options::WriteOptions::content_type`] for more details.
    ///
    /// ## Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// // Set content type for plain text file
    /// let _ = op
    ///     .write_with("path/to/file", vec![0; 4096])
    ///     .content_type("text/plain")
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn content_type(mut self, v: &str) -> Self {
        self.args.0.content_type = Some(v.to_string());
        self
    }

    /// Sets Content-Disposition header for this write request.
    ///
    /// Refer to [`options::WriteOptions::content_disposition`] for more details.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let _ = op
    ///     .write_with("path/to/file", vec![0; 4096])
    ///     .content_disposition("attachment; filename=\"filename.jpg\"")
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn content_disposition(mut self, v: &str) -> Self {
        self.args.0.content_disposition = Some(v.to_string());
        self
    }

    /// Sets Content-Encoding header for this write request.
    ///
    /// Refer to [`options::WriteOptions::content_encoding`] for more details.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let _ = op
    ///     .write_with("path/to/file", vec![0; 4096])
    ///     .content_encoding("gzip")
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn content_encoding(mut self, v: &str) -> Self {
        self.args.0.content_encoding = Some(v.to_string());
        self
    }

    /// Write only when the file at the write path has this exact ETag.
    ///
    /// Refer to [`options::WriteOptions::if_match`] for the full contract.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let _ = op
    ///     .write_with("path/to/file", vec![0; 4096])
    ///     .if_match("\"686897696a7c876b7e\"")
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_match(mut self, s: &str) -> Self {
        self.args.0.if_match = Some(s.to_string());
        self
    }

    /// Write only when the file at the write path does not have this ETag.
    ///
    /// Refer to [`options::WriteOptions::if_none_match`] for the full contract.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let _ = op
    ///     .write_with("path/to/file", vec![0; 4096])
    ///     .if_none_match("\"686897696a7c876b7e\"")
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_none_match(mut self, s: &str) -> Self {
        self.args.0.if_none_match = Some(s.to_string());
        self
    }

    /// Write only when the file at the write path has this exact version.
    ///
    /// Refer to [`options::WriteOptions::if_version_match`] for the full contract.
    pub fn if_version_match(mut self, version: &str) -> Self {
        self.args.0.if_version_match = Some(version.to_string());
        self
    }

    /// Write only when the file at the write path does not have this version.
    ///
    /// Refer to [`options::WriteOptions::if_version_not_match`] for the full contract.
    pub fn if_version_not_match(mut self, version: &str) -> Self {
        self.args.0.if_version_not_match = Some(version.to_string());
        self
    }

    /// Write only when the file still has the identity recorded in `metadata`.
    ///
    /// Refer to [`options::WriteOptions::if_not_changed`] for the full contract.
    pub fn if_not_changed(mut self, metadata: &Metadata) -> Self {
        self.args.0.if_not_changed = Some(metadata.clone());
        self
    }

    /// Write only when no file exists at the write path.
    ///
    /// Refer to [`options::WriteOptions::if_not_exists`] for the full contract.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let _ = op
    ///     .write_with("path/to/file", vec![0; 4096])
    ///     .if_not_exists(true)
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_not_exists(mut self, b: bool) -> Self {
        self.args.0.if_not_exists = b;
        self
    }

    /// Sets user metadata for this write request.
    ///
    /// Refer to [`options::WriteOptions::user_metadata`] for more details.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let _ = op
    ///     .write_with("path/to/file", vec![0; 4096])
    ///     .user_metadata([
    ///         ("language".to_string(), "rust".to_string()),
    ///         ("author".to_string(), "OpenDAL".to_string()),
    ///     ])
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn user_metadata(mut self, data: impl IntoIterator<Item = (String, String)>) -> Self {
        self.args.0.user_metadata = Some(HashMap::from_iter(data));
        self
    }
}

/// [`Operator::writer_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FutureWriter<F> = OperatorFuture<options::WriteOptions, Writer, F>;

impl<F: Future<Output = Result<Writer>>> FutureWriter<F> {
    /// Sets append mode for this write request.
    ///
    /// Refer to [`options::WriteOptions::append`] for more details.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let mut w = op.writer_with("path/to/file").append(true).await?;
    /// w.write(vec![0; 4096]).await?;
    /// w.write(vec![1; 4096]).await?;
    /// w.close().await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn append(mut self, v: bool) -> Self {
        self.args.append = v;
        self
    }

    /// Sets chunk size for buffered writes.
    ///
    /// Refer to [`options::WriteOptions::chunk`] for more details.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// // Set 8MiB chunk size - data will be sent in one API call at close
    /// let mut w = op
    ///     .writer_with("path/to/file")
    ///     .chunk(8 * 1024 * 1024)
    ///     .await?;
    /// w.write(vec![0; 4096]).await?;
    /// w.write(vec![1; 4096]).await?;
    /// w.close().await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn chunk(mut self, v: usize) -> Self {
        self.args.chunk = Some(v);
        self
    }

    /// Sets concurrent write operations for this writer.
    ///
    /// Refer to [`options::WriteOptions::concurrent`] for more details.
    ///
    /// ## Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// // Enable concurrent writes with 8 parallel operations
    /// let mut w = op.writer_with("path/to/file").concurrent(8).await?;
    ///
    /// // First write starts immediately
    /// w.write(vec![0; 4096]).await?;
    ///
    /// // Second write runs concurrently with first
    /// w.write(vec![1; 4096]).await?;
    ///
    /// // Ensures all writes complete successfully and in order
    /// w.close().await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn concurrent(mut self, v: usize) -> Self {
        self.args.concurrent = v.max(1);
        self
    }

    /// Sets Cache-Control header for this write operation.
    ///
    /// Refer to [`options::WriteOptions::cache_control`] for more details.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// // Cache content for 7 days (604800 seconds)
    /// let mut w = op
    ///     .writer_with("path/to/file")
    ///     .cache_control("max-age=604800")
    ///     .await?;
    /// w.write(vec![0; 4096]).await?;
    /// w.write(vec![1; 4096]).await?;
    /// w.close().await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn cache_control(mut self, v: &str) -> Self {
        self.args.cache_control = Some(v.to_string());
        self
    }

    /// Sets `Content-Type` header for this write operation.
    ///
    /// Refer to [`options::WriteOptions::content_type`] for more details.
    ///
    /// ## Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// // Set content type for plain text file
    /// let mut w = op
    ///     .writer_with("path/to/file")
    ///     .content_type("text/plain")
    ///     .await?;
    /// w.write(vec![0; 4096]).await?;
    /// w.write(vec![1; 4096]).await?;
    /// w.close().await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn content_type(mut self, v: &str) -> Self {
        self.args.content_type = Some(v.to_string());
        self
    }

    /// Sets Content-Disposition header for this write request.
    ///
    /// Refer to [`options::WriteOptions::content_disposition`] for more details.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let mut w = op
    ///     .writer_with("path/to/file")
    ///     .content_disposition("attachment; filename=\"filename.jpg\"")
    ///     .await?;
    /// w.write(vec![0; 4096]).await?;
    /// w.write(vec![1; 4096]).await?;
    /// w.close().await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn content_disposition(mut self, v: &str) -> Self {
        self.args.content_disposition = Some(v.to_string());
        self
    }

    /// Sets Content-Encoding header for this write request.
    ///
    /// Refer to [`options::WriteOptions::content_encoding`] for more details.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let mut w = op
    ///     .writer_with("path/to/file")
    ///     .content_encoding("gzip")
    ///     .await?;
    /// w.write(vec![0; 4096]).await?;
    /// w.write(vec![1; 4096]).await?;
    /// w.close().await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn content_encoding(mut self, v: &str) -> Self {
        self.args.content_encoding = Some(v.to_string());
        self
    }

    /// Write only when the file at the write path has this exact ETag.
    ///
    /// Refer to [`options::WriteOptions::if_match`] for the full contract.
    /// Depending on the service, the conditional error may surface when
    /// creating the writer, while writing, or when closing it.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let mut w = op
    ///     .writer_with("path/to/file")
    ///     .if_match("\"686897696a7c876b7e\"")
    ///     .await?;
    /// w.write(vec![0; 4096]).await?;
    /// w.write(vec![1; 4096]).await?;
    /// w.close().await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_match(mut self, s: &str) -> Self {
        self.args.if_match = Some(s.to_string());
        self
    }

    /// Write only when the file at the write path does not have this ETag.
    ///
    /// Refer to [`options::WriteOptions::if_none_match`] for the full contract.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let mut w = op
    ///     .writer_with("path/to/file")
    ///     .if_none_match("\"686897696a7c876b7e\"")
    ///     .await?;
    /// w.write(vec![0; 4096]).await?;
    /// w.write(vec![1; 4096]).await?;
    /// w.close().await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_none_match(mut self, s: &str) -> Self {
        self.args.if_none_match = Some(s.to_string());
        self
    }

    /// Write only when the file at the write path has this exact version.
    ///
    /// Refer to [`options::WriteOptions::if_version_match`] for the full contract.
    pub fn if_version_match(mut self, version: &str) -> Self {
        self.args.if_version_match = Some(version.to_string());
        self
    }

    /// Write only when the file at the write path does not have this version.
    ///
    /// Refer to [`options::WriteOptions::if_version_not_match`] for the full contract.
    pub fn if_version_not_match(mut self, version: &str) -> Self {
        self.args.if_version_not_match = Some(version.to_string());
        self
    }

    /// Write only when the file still has the identity recorded in `metadata`.
    ///
    /// Refer to [`options::WriteOptions::if_not_changed`] for the full contract.
    pub fn if_not_changed(mut self, metadata: &Metadata) -> Self {
        self.args.if_not_changed = Some(metadata.clone());
        self
    }

    /// Write only when no file exists at the write path.
    ///
    /// Refer to [`options::WriteOptions::if_not_exists`] for the full contract.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let mut w = op.writer_with("path/to/file").if_not_exists(true).await?;
    /// w.write(vec![0; 4096]).await?;
    /// w.write(vec![1; 4096]).await?;
    /// w.close().await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_not_exists(mut self, b: bool) -> Self {
        self.args.if_not_exists = b;
        self
    }

    /// Sets user metadata for this write request.
    ///
    /// Refer to [`options::WriteOptions::user_metadata`] for more details.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    /// # use futures::StreamExt;
    /// # use futures::SinkExt;
    /// use bytes::Bytes;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let mut w = op
    ///     .writer_with("path/to/file")
    ///     .user_metadata([
    ///         ("content-type".to_string(), "text/plain".to_string()),
    ///         ("author".to_string(), "OpenDAL".to_string()),
    ///     ])
    ///     .await?;
    /// w.write(vec![0; 4096]).await?;
    /// w.close().await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn user_metadata(mut self, data: impl IntoIterator<Item = (String, String)>) -> Self {
        self.args.user_metadata = Some(HashMap::from_iter(data));
        self
    }
}

/// [`Operator::delete_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FutureDelete<F> = OperatorFuture<options::DeleteOptions, (), F>;

impl<F: Future<Output = Result<()>>> FutureDelete<F> {
    /// Delete the given version of the file instead of the current one.
    ///
    /// Refer to [`options::DeleteOptions::version`] for the full contract.
    pub fn version(mut self, v: &str) -> Self {
        self.args.version = Some(v.to_string());
        self
    }

    /// Enable recursive deletion.
    ///
    /// Refer to [`options::DeleteOptions::recursive`] for the full contract.
    pub fn recursive(mut self, recursive: bool) -> Self {
        self.args.recursive = recursive;
        self
    }

    /// Delete only when the file at the delete path has this exact ETag.
    ///
    /// Refer to [`options::DeleteOptions::if_match`] for the full contract.
    pub fn if_match(mut self, etag: &str) -> Self {
        self.args.if_match = Some(etag.to_string());
        self
    }

    /// Delete only when the file at the delete path does not have this ETag.
    ///
    /// Refer to [`options::DeleteOptions::if_none_match`] for the full contract.
    pub fn if_none_match(mut self, etag: &str) -> Self {
        self.args.if_none_match = Some(etag.to_string());
        self
    }

    /// Delete only when the file at the delete path has this exact version.
    ///
    /// Refer to [`options::DeleteOptions::if_version_match`] for the full contract.
    pub fn if_version_match(mut self, version: &str) -> Self {
        self.args.if_version_match = Some(version.to_string());
        self
    }

    /// Delete only when the file at the delete path does not have this version.
    ///
    /// Refer to [`options::DeleteOptions::if_version_not_match`] for the full contract.
    pub fn if_version_not_match(mut self, version: &str) -> Self {
        self.args.if_version_not_match = Some(version.to_string());
        self
    }

    /// Delete only when the file still has the identity recorded in `metadata`.
    ///
    /// Refer to [`options::DeleteOptions::if_not_changed`] for the full contract.
    pub fn if_not_changed(mut self, metadata: &Metadata) -> Self {
        self.args.if_not_changed = Some(metadata.clone());
        self
    }
}

/// This future creates a [`Deleter`].
///
/// Use its methods to configure the operation before awaiting it.
pub type FutureDeleter<F> = OperatorFuture<OpDeleter, (), F>;

/// [`Operator::list_with`] and [`Operator::lister_with`] return this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FutureList<F> = OperatorFuture<options::ListOptions, Vec<Entry>, F>;

impl<F: Future<Output = Result<Vec<Entry>>>> FutureList<F> {
    /// The limit passed to underlying service to specify the max results
    /// that could return per-request.
    ///
    /// Users could use this to control the memory usage of list operation.
    pub fn limit(mut self, v: usize) -> Self {
        self.args.limit = Some(v);
        self
    }

    /// The start_after passes to underlying service to specify the specified key
    /// to start listing from.
    pub fn start_after(mut self, v: &str) -> Self {
        self.args.start_after = Some(v.to_string());
        self
    }

    /// The recursive is used to control whether the list operation is recursive.
    ///
    /// - If `false`, list operation will only list the entries under the given path.
    /// - If `true`, list operation will list all entries that starts with given path.
    ///
    /// Default to `false`.
    pub fn recursive(mut self, v: bool) -> Self {
        self.args.recursive = v;
        self
    }

    /// Controls whether the `list` operation should return file versions.
    ///
    /// This function allows you to specify if the `list` operation, when executed, should include
    /// information about different versions of files, if versioning is supported and enabled.
    ///
    /// If `true`, subsequent `list` operations will include version information for each file.
    /// If `false`, version information will be omitted from the `list` results.
    ///
    /// Default to `false`
    pub fn versions(mut self, v: bool) -> Self {
        self.args.versions = v;
        self
    }

    /// Controls whether the `list` operation should include deleted files (or versions).
    ///
    /// This function allows you to specify if the `list` operation, when executed, should include
    /// entries for files or versions that have been marked as deleted. This is particularly relevant
    /// in object storage systems that support soft deletion or versioning.
    ///
    /// If `true`, subsequent `list` operations will include deleted files or versions.
    /// If `false`, deleted files or versions will be excluded from the `list` results.
    pub fn deleted(mut self, v: bool) -> Self {
        self.args.deleted = v;
        self
    }
}

/// [`Operator::list_with`] and [`Operator::lister_with`] return this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FutureLister<F> = OperatorFuture<options::ListOptions, Lister, F>;

impl<F: Future<Output = Result<Lister>>> FutureLister<F> {
    /// The limit passed to underlying service to specify the max results
    /// that could return per-request.
    ///
    /// Users could use this to control the memory usage of list operation.
    pub fn limit(mut self, v: usize) -> Self {
        self.args.limit = Some(v);
        self
    }

    /// The start_after passes to underlying service to specify the specified key
    /// to start listing from.
    pub fn start_after(mut self, v: &str) -> Self {
        self.args.start_after = Some(v.to_string());
        self
    }

    /// The recursive is used to control whether the list operation is recursive.
    ///
    /// - If `false`, list operation will only list the entries under the given path.
    /// - If `true`, list operation will list all entries that starts with given path.
    ///
    /// Default to `false`.
    pub fn recursive(mut self, v: bool) -> Self {
        self.args.recursive = v;
        self
    }

    /// Controls whether the `list` operation should return file versions.
    ///
    /// This function allows you to specify if the `list` operation, when executed, should include
    /// information about different versions of files, if versioning is supported and enabled.
    ///
    /// If `true`, subsequent `list` operations will include version information for each file.
    /// If `false`, version information will be omitted from the `list` results.
    ///
    /// Default to `false`
    pub fn versions(mut self, v: bool) -> Self {
        self.args.versions = v;
        self
    }

    /// Controls whether the `list` operation should include deleted files (or versions).
    ///
    /// This function allows you to specify if the `list` operation, when executed, should include
    /// entries for files or versions that have been marked as deleted. This is particularly relevant
    /// in object storage systems that support soft deletion or versioning.
    ///
    /// If `true`, subsequent `list` operations will include deleted files or versions.
    /// If `false`, deleted files or versions will be excluded from the `list` results.
    pub fn deleted(mut self, v: bool) -> Self {
        self.args.deleted = v;
        self
    }
}

/// [`Operator::compose_with`] returns this future.
///
/// Use its methods to configure the destination before awaiting it.
pub type FutureCompose<F> = OperatorFuture<
    (
        options::ComposeOptions,
        Vec<(String, options::ComposeSourceOptions)>,
    ),
    Metadata,
    F,
>;

impl<F: Future<Output = Result<Metadata>>> FutureCompose<F> {
    /// Set destination Cache-Control metadata.
    pub fn cache_control(mut self, value: &str) -> Self {
        self.args.0.cache_control = Some(value.to_string());
        self
    }

    /// Set destination Content-Type metadata.
    pub fn content_type(mut self, value: &str) -> Self {
        self.args.0.content_type = Some(value.to_string());
        self
    }

    /// Set destination Content-Disposition metadata.
    pub fn content_disposition(mut self, value: &str) -> Self {
        self.args.0.content_disposition = Some(value.to_string());
        self
    }

    /// Set destination Content-Encoding metadata.
    pub fn content_encoding(mut self, value: &str) -> Self {
        self.args.0.content_encoding = Some(value.to_string());
        self
    }

    /// Set destination user metadata.
    pub fn user_metadata(mut self, value: HashMap<String, String>) -> Self {
        self.args.0.user_metadata = Some(value);
        self
    }

    /// Require a missing destination object.
    pub fn if_not_exists(mut self, value: bool) -> Self {
        self.args.0.if_not_exists = value;
        self
    }

    /// Require this destination ETag.
    pub fn if_match(mut self, value: &str) -> Self {
        self.args.0.if_match = Some(value.to_string());
        self
    }

    /// Require a different destination ETag.
    pub fn if_none_match(mut self, value: &str) -> Self {
        self.args.0.if_none_match = Some(value.to_string());
        self
    }

    /// Require this destination version.
    pub fn if_version_match(mut self, value: &str) -> Self {
        self.args.0.if_version_match = Some(value.to_string());
        self
    }

    /// Require a different destination version.
    pub fn if_version_not_match(mut self, value: &str) -> Self {
        self.args.0.if_version_not_match = Some(value.to_string());
        self
    }

    /// Require the destination to retain the identity in `metadata`.
    pub fn if_not_changed(mut self, metadata: &Metadata) -> Self {
        self.args.0.if_not_changed = Some(metadata.clone());
        self
    }

    /// Set the maximum number of independent backend tasks.
    pub fn concurrent(mut self, value: usize) -> Self {
        self.args.0.concurrent = value.max(1);
        self
    }
}

/// [`Operator::composer_with`] returns this future.
///
/// Use its methods to configure the destination before awaiting it.
pub type FutureComposer<F> = OperatorFuture<options::ComposeOptions, Composer, F>;

impl<F: Future<Output = Result<Composer>>> FutureComposer<F> {
    /// Set destination Cache-Control metadata.
    pub fn cache_control(mut self, value: &str) -> Self {
        self.args.cache_control = Some(value.to_string());
        self
    }

    /// Set destination Content-Type metadata.
    pub fn content_type(mut self, value: &str) -> Self {
        self.args.content_type = Some(value.to_string());
        self
    }

    /// Set destination Content-Disposition metadata.
    pub fn content_disposition(mut self, value: &str) -> Self {
        self.args.content_disposition = Some(value.to_string());
        self
    }

    /// Set destination Content-Encoding metadata.
    pub fn content_encoding(mut self, value: &str) -> Self {
        self.args.content_encoding = Some(value.to_string());
        self
    }

    /// Set destination user metadata.
    pub fn user_metadata(mut self, value: HashMap<String, String>) -> Self {
        self.args.user_metadata = Some(value);
        self
    }

    /// Require a missing destination object.
    pub fn if_not_exists(mut self, value: bool) -> Self {
        self.args.if_not_exists = value;
        self
    }

    /// Require this destination ETag.
    pub fn if_match(mut self, value: &str) -> Self {
        self.args.if_match = Some(value.to_string());
        self
    }

    /// Require a different destination ETag.
    pub fn if_none_match(mut self, value: &str) -> Self {
        self.args.if_none_match = Some(value.to_string());
        self
    }

    /// Require this destination version.
    pub fn if_version_match(mut self, value: &str) -> Self {
        self.args.if_version_match = Some(value.to_string());
        self
    }

    /// Require a different destination version.
    pub fn if_version_not_match(mut self, value: &str) -> Self {
        self.args.if_version_not_match = Some(value.to_string());
        self
    }

    /// Require the destination to retain the identity in `metadata`.
    pub fn if_not_changed(mut self, metadata: &Metadata) -> Self {
        self.args.if_not_changed = Some(metadata.clone());
        self
    }

    /// Set the maximum number of independent backend tasks.
    pub fn concurrent(mut self, value: usize) -> Self {
        self.args.concurrent = value.max(1);
        self
    }
}

/// [`Operator::copy_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FutureCopy<F> = OperatorFuture<(options::CopyOptions, String), Metadata, F>;

impl<F: Future<Output = Result<Metadata>>> FutureCopy<F> {
    /// Copy only when no file exists at the destination path.
    ///
    /// Refer to [`options::CopyOptions::if_not_exists`] for the full contract.
    ///
    /// ### Example
    ///
    /// ```
    /// # use opendal_core::Result;
    /// # use opendal_core::Operator;
    ///
    /// # async fn test(op: Operator) -> Result<()> {
    /// let _ = op
    ///     .copy_with("source/path", "target/path")
    ///     .if_not_exists(true)
    ///     .await?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn if_not_exists(mut self, v: bool) -> Self {
        self.args.0.if_not_exists = v;
        self
    }

    /// Copy only when the destination file has this exact ETag.
    ///
    /// Refer to [`options::CopyOptions::if_match`] for the full contract.
    pub fn if_match(mut self, etag: &str) -> Self {
        self.args.0.if_match = Some(etag.to_string());
        self
    }

    /// Copy only when the destination file does not have this ETag.
    ///
    /// Refer to [`options::CopyOptions::if_none_match`] for the full contract.
    pub fn if_none_match(mut self, etag: &str) -> Self {
        self.args.0.if_none_match = Some(etag.to_string());
        self
    }

    /// Copy only when the destination file has this exact version.
    ///
    /// Refer to [`options::CopyOptions::if_version_match`] for the full contract.
    pub fn if_version_match(mut self, version: &str) -> Self {
        self.args.0.if_version_match = Some(version.to_string());
        self
    }

    /// Copy only when the destination file does not have this version.
    ///
    /// Refer to [`options::CopyOptions::if_version_not_match`] for the full contract.
    pub fn if_version_not_match(mut self, version: &str) -> Self {
        self.args.0.if_version_not_match = Some(version.to_string());
        self
    }

    /// Copy only when the destination file still has the identity recorded in `metadata`.
    ///
    /// Refer to [`options::CopyOptions::if_not_changed`] for the full contract.
    pub fn if_not_changed(mut self, metadata: &Metadata) -> Self {
        self.args.0.if_not_changed = Some(metadata.clone());
        self
    }

    /// Copy from this source file version instead of the current one.
    ///
    /// Refer to [`options::CopyOptions::source_version`] for the full contract.
    pub fn source_version(mut self, version: impl Into<String>) -> Self {
        self.args.0.source_version = Some(version.into());
        self
    }

    /// Sets concurrent copy operations for this copy.
    ///
    /// Refer to [`options::CopyOptions::concurrent`] for more details.
    pub fn concurrent(mut self, v: usize) -> Self {
        self.args.0.concurrent = v.max(1);
        self
    }

    /// Sets chunk size for segmented copy operations.
    ///
    /// Refer to [`options::CopyOptions::chunk`] for more details.
    pub fn chunk(mut self, v: usize) -> Self {
        self.args.0.chunk = Some(v);
        self
    }

    /// Sets known source content length as a hint for this `copy` request.
    ///
    /// Refer to [`options::CopyOptions::source_content_length_hint`] for more details.
    pub fn source_content_length_hint(mut self, v: u64) -> Self {
        self.args.0.source_content_length_hint = Some(v);
        self
    }
}

/// [`Operator::copier_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FutureCopier<F> = OperatorFuture<(options::CopyOptions, String), Copier, F>;

impl<F: Future<Output = Result<Copier>>> FutureCopier<F> {
    /// Copy only when no file exists at the destination path.
    ///
    /// Refer to [`options::CopyOptions::if_not_exists`] for the full contract.
    pub fn if_not_exists(mut self, v: bool) -> Self {
        self.args.0.if_not_exists = v;
        self
    }

    /// Copy only when the destination file has this exact ETag.
    ///
    /// Refer to [`options::CopyOptions::if_match`] for the full contract.
    pub fn if_match(mut self, etag: &str) -> Self {
        self.args.0.if_match = Some(etag.to_string());
        self
    }

    /// Copy only when the destination file does not have this ETag.
    ///
    /// Refer to [`options::CopyOptions::if_none_match`] for the full contract.
    pub fn if_none_match(mut self, etag: &str) -> Self {
        self.args.0.if_none_match = Some(etag.to_string());
        self
    }

    /// Copy only when the destination file has this exact version.
    ///
    /// Refer to [`options::CopyOptions::if_version_match`] for the full contract.
    pub fn if_version_match(mut self, version: &str) -> Self {
        self.args.0.if_version_match = Some(version.to_string());
        self
    }

    /// Copy only when the destination file does not have this version.
    ///
    /// Refer to [`options::CopyOptions::if_version_not_match`] for the full contract.
    pub fn if_version_not_match(mut self, version: &str) -> Self {
        self.args.0.if_version_not_match = Some(version.to_string());
        self
    }

    /// Copy only when the destination file still has the identity recorded in `metadata`.
    ///
    /// Refer to [`options::CopyOptions::if_not_changed`] for the full contract.
    pub fn if_not_changed(mut self, metadata: &Metadata) -> Self {
        self.args.0.if_not_changed = Some(metadata.clone());
        self
    }

    /// Copy from this source file version instead of the current one.
    ///
    /// Refer to [`options::CopyOptions::source_version`] for the full contract.
    pub fn source_version(mut self, version: impl Into<String>) -> Self {
        self.args.0.source_version = Some(version.into());
        self
    }

    /// Sets known source content length as a hint for this `copier`.
    ///
    /// Refer to [`options::CopyOptions::source_content_length_hint`] for more details.
    pub fn source_content_length_hint(mut self, v: u64) -> Self {
        self.args.0.source_content_length_hint = Some(v);
        self
    }

    /// Sets concurrent copy operations for this copier.
    ///
    /// Refer to [`options::CopyOptions::concurrent`] for more details.
    pub fn concurrent(mut self, v: usize) -> Self {
        self.args.0.concurrent = v.max(1);
        self
    }

    /// Sets chunk size for segmented copy operations.
    ///
    /// Refer to [`options::CopyOptions::chunk`] for more details.
    pub fn chunk(mut self, v: usize) -> Self {
        self.args.0.chunk = Some(v);
        self
    }
}

/// [`Operator::rename_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FutureRename<F> = OperatorFuture<(options::RenameOptions, String), (), F>;

impl<F: Future<Output = Result<()>>> FutureRename<F> {
    /// Rename only when no file exists at the destination path.
    ///
    /// Refer to [`options::RenameOptions::if_not_exists`] for the full contract.
    ///
    /// ### Example
    ///
    /// ```
    /// use opendal_core::Operator;
    /// use opendal_core::Result;
    ///
    /// async fn rename_with_options(op: Operator) -> Result<()> {
    ///     op.rename_with("source/path", "target/path")
    ///         .if_not_exists(true)
    ///         .await?;
    ///     Ok(())
    /// }
    /// ```
    pub fn if_not_exists(mut self, v: bool) -> Self {
        self.args.0.if_not_exists = v;
        self
    }
}

/// [`Operator::restore_with`] returns this future.
///
/// Use its methods to configure the operation before awaiting it.
pub type FutureRestore<F> = OperatorFuture<options::RestoreOptions, (), F>;

impl<F: Future<Output = Result<()>>> FutureRestore<F> {
    /// Restore this historical version as the current version.
    ///
    /// Refer to [`options::RestoreOptions::version`] for the full contract.
    pub fn version(mut self, version: impl Into<String>) -> Self {
        self.args.version = Some(version.into());
        self
    }

    /// Restore the selected version only when no file currently exists at the restore path.
    ///
    /// Refer to [`options::RestoreOptions::if_not_exists`] for the full contract.
    pub fn if_not_exists(mut self, if_not_exists: bool) -> Self {
        self.args.if_not_exists = if_not_exists;
        self
    }
}
