You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by xu...@apache.org on 2025/05/26 06:29:53 UTC

(opendal) 02/02: docs: Remove deprecated APIs and polish docs for public APIs

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

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

commit a6217ac8255877fc3bde700956a870c74550c817
Author: Xuanwo <gi...@xuanwo.io>
AuthorDate: Mon May 26 14:29:20 2025 +0800

    docs: Remove deprecated APIs and polish docs for public APIs
    
    Signed-off-by: Xuanwo <gi...@xuanwo.io>
---
 core/src/lib.rs                     |   4 +-
 core/src/raw/path.rs                |   1 +
 core/src/types/operator/operator.rs | 771 ++++++++++++++++--------------------
 3 files changed, 351 insertions(+), 425 deletions(-)

diff --git a/core/src/lib.rs b/core/src/lib.rs
index ecb2a5910..864dfb452 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -92,6 +92,7 @@
 //!
 //! Every Operator API follows a consistent pattern. For example, consider the `read` operation:
 //!
+//! - [`Operator::read`]: Executes a read operation.
 //! - [`Operator::read_with`]: Executes a read operation with additional options using the builder pattern.
 //! - [`Operator::read_options`]: Executes a read operation with extra options provided via a [`options::ReadOptions`] struct.
 //! - [`Operator::reader`]: Creates a reader for streaming data, allowing for flexible access.
@@ -99,7 +100,7 @@
 //! - [`Operator::reader_options`]: Creates a reader with extra options provided via a [`options::ReadOptions`] struct.
 //!
 //! The [`Reader`] created by [`Operator`] supports custom read control methods and can be converted
-//! into `futures::AsyncRead` for broader ecosystem compatibility.
+//! into [`futures::AsyncRead`] or [`futures::Stream`] for broader ecosystem compatibility.
 //!
 //! ```no_run
 //! use opendal::layers::LoggingLayer;
@@ -137,6 +138,7 @@
 //!             range: (0..8 * 1024 * 1024).into(),
 //!             chunk: Some(1024 * 1024),
 //!             concurrent: 4,
+//!             ..Default::default()
 //!         })
 //!         .await?;
 //!
diff --git a/core/src/raw/path.rs b/core/src/raw/path.rs
index 5790c7232..c49c94e3e 100644
--- a/core/src/raw/path.rs
+++ b/core/src/raw/path.rs
@@ -211,6 +211,7 @@ pub fn get_parent(path: &str) -> &str {
 }
 
 /// Validate given path is match with given EntryMode.
+#[inline]
 pub fn validate_path(path: &str, mode: EntryMode) -> bool {
     debug_assert!(!path.is_empty(), "input path should not be empty");
 
diff --git a/core/src/types/operator/operator.rs b/core/src/types/operator/operator.rs
index af9d08fa3..f87706431 100644
--- a/core/src/types/operator/operator.rs
+++ b/core/src/types/operator/operator.rs
@@ -137,6 +137,7 @@ use crate::*;
 ///             range: (0..8 * 1024 * 1024).into(),
 ///             chunk: Some(1024 * 1024),
 ///             concurrent: 4,
+///             ..Default::default()
 ///         })
 ///         .await?;
 ///
@@ -238,7 +239,7 @@ impl Operator {
     /// # }
     /// ```
     pub async fn check(&self) -> Result<()> {
-        let mut ds = self.lister("/").await?;
+        let mut ds = self.lister_with("/").limit(1).await?;
 
         match ds.next().await {
             Some(Err(e)) if e.kind() != ErrorKind::NotFound => Err(e),
@@ -246,14 +247,14 @@ impl Operator {
         }
     }
 
-    /// Get given path's metadata.
+    /// Retrieve the metadata for the specified path.
     ///
     /// # Notes
     ///
     /// ## Extra Options
     ///
-    /// [`Operator::stat`] is a wrapper of [`Operator::stat_with`] without any options. To use extra
-    /// options like `if_match` and `if_none_match`, please use [`Operator::stat_with`] instead.
+    /// [`Operator::stat`] is a wrapper around [`Operator::stat_with`] that uses no additional options.
+    /// To specify extra options such as `if_match` and `if_none_match`, please use [`Operator::stat_with`] instead.
     ///
     /// # Examples
     ///
@@ -278,7 +279,7 @@ impl Operator {
         self.stat_with(path).await
     }
 
-    /// Get given path's metadata with extra options.
+    /// Retrieve the metadata of the specified path with additional options.
     ///
     /// # Options
     ///
@@ -313,34 +314,6 @@ impl Operator {
     /// # Ok(())
     /// # }
     /// ```
-    ///
-    /// ---
-    ///
-    /// # Behavior
-    ///
-    /// ## Services that support `create_dir`
-    ///
-    /// `test` and `test/` may vary in some services such as S3. However, on a local file system,
-    /// they're identical. Therefore, the behavior of `stat("test")` and `stat("test/")` might differ
-    /// in certain edge cases. Always use `stat("test/")` when you need to access a directory if possible.
-    ///
-    /// Here are the behavior list:
-    ///
-    /// | Case                   | Path            | Result                                     |
-    /// |------------------------|-----------------|--------------------------------------------|
-    /// | stat existing dir      | `abc/`          | Metadata with dir mode                     |
-    /// | stat existing file     | `abc/def_file`  | Metadata with file mode                    |
-    /// | stat dir without `/`   | `abc/def_dir`   | Error `NotFound` or metadata with dir mode |
-    /// | stat file with `/`     | `abc/def_file/` | Error `NotFound`                           |
-    /// | stat not existing path | `xyz`           | Error `NotFound`                           |
-    ///
-    /// Refer to [RFC: List Prefix][crate::docs::rfcs::rfc_3243_list_prefix] for more details.
-    ///
-    /// ## Services that not support `create_dir`
-    ///
-    /// For services that not support `create_dir`, `stat("test/")` will return `NotFound` even
-    /// when `test/abc` exists since the service won't have the concept of dir. There is nothing
-    /// we can do about this.
     pub fn stat_with(&self, path: &str) -> FutureStat<impl Future<Output = Result<Metadata>>> {
         let path = normalize_path(path);
         OperatorFuture::new(
@@ -351,7 +324,44 @@ impl Operator {
         )
     }
 
-    /// Get given path's metadata with extra options.
+    /// Retrieve the metadata of the specified path with additional options.
+    ///
+    /// # Examples
+    ///
+    /// ## Get metadata while `ETag` matches
+    ///
+    /// `stat_with` will
+    ///
+    /// - return `Ok(metadata)` if `ETag` matches
+    /// - return `Err(error)` and `error.kind() == ErrorKind::ConditionNotMatch` if file exists but
+    ///   `ETag` mismatch
+    /// - return `Err(err)` if other errors occur, for example, `NotFound`.
+    ///
+    /// ```
+    /// # use anyhow::Result;
+    /// # use futures::io;
+    /// # use opendal::Operator;
+    /// use opendal::options;
+    /// use opendal::ErrorKind;
+    /// #
+    /// # async fn test(op: Operator) -> Result<()> {
+    /// let res = op
+    ///     .stat_options("test", options::StatOptions {
+    ///         if_match: Some("<etag>".to_string()),
+    ///         ..Default::default()
+    ///     })
+    ///     .await;
+    /// if let Err(e) = res {
+    ///     if e.kind() == ErrorKind::ConditionNotMatch {
+    ///         println!("file exists, but etag mismatch")
+    ///     }
+    ///     if e.kind() == ErrorKind::NotFound {
+    ///         println!("file not exist")
+    ///     }
+    /// }
+    /// # Ok(())
+    /// # }
+    /// ```
     pub async fn stat_options(&self, path: &str, opts: options::StatOptions) -> Result<Metadata> {
         let path = normalize_path(path);
         Self::stat_inner(self.accessor.clone(), path, opts).await
@@ -367,7 +377,7 @@ impl Operator {
         Ok(rp.into_metadata())
     }
 
-    /// Check if this path exists or not.
+    /// Check whether this path exists.
     ///
     /// # Example
     ///
@@ -386,52 +396,22 @@ impl Operator {
         let r = self.stat(path).await;
         match r {
             Ok(_) => Ok(true),
-            Err(err) => match err.kind() {
-                ErrorKind::NotFound => Ok(false),
-                _ => Err(err),
-            },
+            Err(err) if err.kind() == ErrorKind::NotFound => Ok(false),
+            Err(err) => Err(err),
         }
     }
 
-    /// Check if this path exists or not.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use anyhow::Result;
-    /// use futures::io;
-    /// use opendal::Operator;
-    ///
-    /// async fn test(op: Operator) -> Result<()> {
-    ///     let _ = op.is_exist("test").await?;
-    ///
-    ///     Ok(())
-    /// }
-    /// ```
-    #[deprecated(note = "rename to `exists` for consistence with `std::fs::exists`")]
-    pub async fn is_exist(&self, path: &str) -> Result<bool> {
-        let r = self.stat(path).await;
-        match r {
-            Ok(_) => Ok(true),
-            Err(err) => match err.kind() {
-                ErrorKind::NotFound => Ok(false),
-                _ => Err(err),
-            },
-        }
-    }
-
-    /// Create a dir at given path.
+    /// Create a directory at the specified path.
     ///
     /// # Notes
     ///
-    /// To indicate that a path is a directory, it is compulsory to include
-    /// a trailing / in the path. Failure to do so may result in
-    /// `NotADirectory` error being returned by OpenDAL.
+    /// To specify that a path is a directory, you must include a trailing slash (/).
+    /// Omitting the trailing slash may cause OpenDAL to return a `NotADirectory` error.
     ///
     /// # Behavior
     ///
-    /// - Create on existing dir will succeed.
-    /// - Create dir is always recursive, works like `mkdir -p`
+    /// - Creating a directory that already exists will succeed.
+    /// - Directory creation is always recursive, functioning like `mkdir -p`.
     ///
     /// # Examples
     ///
@@ -461,19 +441,17 @@ impl Operator {
         Ok(())
     }
 
-    /// Read the whole path into a bytes.
+    /// Read the entire file into bytes from given path.
     ///
     /// # Notes
     ///
-    /// ## Extra Options
+    /// ## Additional Options
     ///
-    /// [`Operator::read`] is a wrapper of [`Operator::read_with`] without any options. To use
-    /// extra options like `range` and `if_match`, please use [`Operator::read_with`] instead.
+    /// [`Operator::read`] is a simplified method that does not support additional options. To access features like `range` and `if_match`, please use [`Operator::read_with`] or [`Operator::read_options`] instead.
     ///
     /// ## Streaming Read
     ///
-    /// This function will allocate a new bytes internally. For more precise memory control or
-    /// reading data lazily, please use [`Operator::reader`]
+    /// This function reads all content into memory at once. For more precise memory management or to read big file lazily, please use [`Operator::reader`].
     ///
     /// # Examples
     ///
@@ -487,20 +465,17 @@ impl Operator {
     /// # }
     /// ```
     pub async fn read(&self, path: &str) -> Result<Buffer> {
-        self.read_with(path).await
+        self.read_options(path, options::ReadOptions::default())
+            .await
     }
 
-    /// Read the whole path into a bytes with extra options.
-    ///
-    /// This function will allocate a new bytes internally. For more precise memory control or
-    /// reading data lazily, please use [`Operator::reader`]
+    /// Read the entire file into bytes from given path with additional options.
     ///
     /// # Notes
     ///
     /// ## Streaming Read
     ///
-    /// This function will allocate a new bytes internally. For more precise memory control or
-    /// reading data lazily, please use [`Operator::reader`]
+    /// This function reads all content into memory at once. For more precise memory management or to read big file lazily, please use [`Operator::reader`].
     ///
     /// # Options
     ///
@@ -508,14 +483,12 @@ impl Operator {
     ///
     /// # Examples
     ///
-    /// Read the whole path into a bytes.
+    /// Read the first 10 bytes of a file:
     ///
     /// ```
     /// # use opendal::Result;
     /// # use opendal::Operator;
-    /// # use futures::TryStreamExt;
     /// # async fn test(op: Operator) -> Result<()> {
-    /// let bs = op.read_with("path/to/file").await?;
     /// let bs = op.read_with("path/to/file").range(0..10).await?;
     /// # Ok(())
     /// # }
@@ -531,7 +504,32 @@ impl Operator {
         )
     }
 
-    /// Read the whole path into a bytes with extra options.
+    /// Read the entire file into bytes from given path with additional options.
+    ///
+    /// # Notes
+    ///
+    /// ## Streaming Read
+    ///
+    /// This function reads all content into memory at once. For more precise memory management or to read big file lazily, please use [`Operator::reader`].
+    ///
+    /// # Examples
+    ///
+    /// Read the first 10 bytes of a file:
+    ///
+    /// ```
+    /// # use opendal::Result;
+    /// # use opendal::Operator;
+    /// use opendal::options;
+    /// # async fn test(op: Operator) -> Result<()> {
+    /// let bs = op
+    ///     .read_options("path/to/file", options::ReadOptions {
+    ///         range: (0..10).into(),
+    ///         ..Default::default()
+    ///     })
+    ///     .await?;
+    /// # Ok(())
+    /// # }
+    /// ```
     pub async fn read_options(&self, path: &str, opts: options::ReadOptions) -> Result<Buffer> {
         let path = normalize_path(path);
         Self::read_inner(self.inner().clone(), path, opts).await
@@ -556,14 +554,13 @@ impl Operator {
         Ok(buf)
     }
 
-    /// Create a new reader which can read the whole path.
+    /// Create a new reader of given path.
     ///
     /// # Notes
     ///
     /// ## Extra Options
     ///
-    /// [`Operator::reader`] is a wrapper of [`Operator::reader_with`] without any options. To use
-    /// extra options like `concurrent`, please use [`Operator::reader_with`] instead.
+    /// [`Operator::reader`] is a simplified method without any options. To use additional options such as `concurrent` or `if_match`, please use [`Operator::reader_with`] or [`Operator::reader_options`] instead.
     ///
     /// # Examples
     ///
@@ -574,6 +571,8 @@ impl Operator {
     /// # use opendal::Scheme;
     /// # async fn test(op: Operator) -> Result<()> {
     /// let r = op.reader("path/to/file").await?;
+    /// // Read the first 10 bytes of the file
+    /// let data = r.read(0..10).await?;
     /// # Ok(())
     /// # }
     /// ```
@@ -581,14 +580,7 @@ impl Operator {
         self.reader_with(path).await
     }
 
-    /// Create a new reader with extra options
-    ///
-    /// # Notes
-    ///
-    /// ## Extra Options
-    ///
-    /// [`Operator::reader`] is a wrapper of [`Operator::reader_with`] without any options. To use
-    /// extra options like `version`, please use [`Operator::reader_with`] instead.
+    /// Create a new reader of given path with additional options.
     ///
     /// # Options
     ///
@@ -596,12 +588,16 @@ impl Operator {
     ///
     /// # Examples
     ///
+    /// Create a reader with a specific version ID:
+    ///
     /// ```
     /// # use opendal::Result;
     /// # use opendal::Operator;
     /// # use opendal::Scheme;
     /// # async fn test(op: Operator) -> Result<()> {
     /// let r = op.reader_with("path/to/file").version("version_id").await?;
+    /// // Read the first 10 bytes of the file
+    /// let data = r.read(0..10).await?;
     /// # Ok(())
     /// # }
     /// ```
@@ -616,7 +612,28 @@ impl Operator {
         )
     }
 
-    /// Create a new reader with extra options
+    /// Create a new reader of given path with additional options.
+    ///
+    /// # Examples
+    ///
+    /// Create a reader with a specific version ID:
+    ///
+    /// ```
+    /// # use opendal::Result;
+    /// # use opendal::Operator;
+    /// use opendal::options;
+    /// # async fn test(op: Operator) -> Result<()> {
+    /// let r = op
+    ///     .reader_options("path/to/file", options::ReadOptions {
+    ///         version: Some("version_id".to_string()),
+    ///         ..Default::default()
+    ///     })
+    ///     .await?;
+    /// // Read the first 10 bytes of the file
+    /// let data = r.read(0..10).await?;
+    /// # Ok(())
+    /// # }
+    /// ```
     pub async fn reader_options(&self, path: &str, opts: options::ReaderOptions) -> Result<Reader> {
         let path = normalize_path(path);
         Self::reader_inner(self.inner().clone(), path, opts).await
@@ -645,25 +662,27 @@ impl Operator {
         Ok(Reader::new(context))
     }
 
-    /// Write bytes into path.
+    /// Write all data to the specified path at once.
     ///
     /// # Notes
     ///
+    /// Visit [`performance::concurrent_write`][crate::docs::performance::concurrent_write] for more details on concurrent writes.
+    ///
     /// ## Extra Options
     ///
-    /// [`Operator::write`] is a simplified version of [`Operator::write_with`] without additional options.
-    /// For advanced features like `content_type` and `cache_control`, use [`Operator::write_with`] instead.
+    /// [`Operator::write`] is a simplified method that does not include additional options.
+    /// For advanced features such as `chunk` and `concurrent`, use [`Operator::write_with`] or [`Operator::write_options`] instead.
     ///
     /// ## Streaming Write
     ///
-    /// This method performs a single bulk write operation. For finer-grained memory control
-    /// or streaming data writes, use [`Operator::writer`] instead.
+    /// This method executes a single bulk write operation. For more precise memory management
+    /// or to write data in a streaming fashion, use [`Operator::writer`] instead.
     ///
     /// ## Multipart Uploads
     ///
-    /// OpenDAL provides multipart upload functionality through the [`Writer`] abstraction,
-    /// handling all upload details automatically. You can customize the upload behavior by
-    /// configuring `chunk` size and `concurrent` operations via [`Operator::writer_with`].
+    /// OpenDAL offers multipart upload capabilities through the [`Writer`] abstraction,
+    /// automatically managing all upload details for you. You can fine-tune the upload process
+    /// by adjusting the `chunk` size and the number of `concurrent` operations using [`Operator::writer_with`].
     ///
     /// # Examples
     ///
@@ -684,39 +703,31 @@ impl Operator {
         self.write_with(path, bs).await
     }
 
-    /// Write data with extra options.
+    /// Write all data to the specified path at once with additional options.
     ///
     /// # Notes
     ///
+    /// Visit [`performance::concurrent_write`][crate::docs::performance::concurrent_write] for more details on concurrent writes.
+    ///
     /// ## Streaming Write
     ///
-    /// This method performs a single bulk write operation for all bytes. For finer-grained
-    /// memory control or lazy writing, consider using [`Operator::writer_with`] instead.
+    /// This method executes a single bulk write operation. For more precise memory management
+    /// or to write data in a streaming fashion, use [`Operator::writer`] instead.
     ///
     /// ## Multipart Uploads
     ///
-    /// OpenDAL handles multipart uploads through the [`Writer`] abstraction, managing all
-    /// the upload details automatically. You can customize the upload behavior by configuring
-    /// `chunk` size and `concurrent` operations via [`Operator::writer_with`].
+    /// OpenDAL offers multipart upload capabilities through the [`Writer`] abstraction,
+    /// automatically managing all upload details for you. You can fine-tune the upload process
+    /// by adjusting the `chunk` size and the number of `concurrent` operations using [`Operator::writer_with`].
     ///
     /// # Options
     ///
-    /// Visit [`FutureWrite`] for all available options.
-    ///
-    /// - [`append`](./operator_futures/type.FutureWrite.html#method.append): Sets append mode for this write request.
-    /// - [`chunk`](./operator_futures/type.FutureWrite.html#method.chunk): Sets chunk size for buffered writes.
-    /// - [`concurrent`](./operator_futures/type.FutureWrite.html#method.concurrent): Sets concurrent write operations for this writer.
-    /// - [`cache_control`](./operator_futures/type.FutureWrite.html#method.cache_control): Sets cache control for this write request.
-    /// - [`content_type`](./operator_futures/type.FutureWrite.html#method.content_type): Sets content type for this write request.
-    /// - [`content_disposition`](./operator_futures/type.FutureWrite.html#method.content_disposition): Sets content disposition for this write request.
-    /// - [`content_encoding`](./operator_futures/type.FutureWrite.html#method.content_encoding): Sets content encoding for this write request.
-    /// - [`if_match`](./operator_futures/type.FutureWrite.html#method.if_match): Sets if-match for this write request.
-    /// - [`if_none_match`](./operator_futures/type.FutureWrite.html#method.if_none_match): Sets if-none-match for this write request.
-    /// - [`if_not_exist`](./operator_futures/type.FutureWrite.html#method.if_not_exist): Sets if-not-exist for this write request.
-    /// - [`user_metadata`](./operator_futures/type.FutureWrite.html#method.user_metadata): Sets user metadata for this write request.
+    /// Visit [`options::WriteOptions`] for all available options.
     ///
     /// # Examples
     ///
+    /// Write data to a file only when it does not already exist:
+    ///
     /// ```
     /// # use opendal::Result;
     /// # use opendal::Operator;
@@ -746,7 +757,42 @@ impl Operator {
         )
     }
 
-    /// Write data with extra options.
+    /// Write all data to the specified path at once with additional options.
+    ///
+    /// # Notes
+    ///
+    /// Visit [`performance::concurrent_write`][crate::docs::performance::concurrent_write] for more details on concurrent writes.
+    ///
+    /// ## Streaming Write
+    ///
+    /// This method executes a single bulk write operation. For more precise memory management
+    /// or to write data in a streaming fashion, use [`Operator::writer`] instead.
+    ///
+    /// ## Multipart Uploads
+    ///
+    /// OpenDAL offers multipart upload capabilities through the [`Writer`] abstraction,
+    /// automatically managing all upload details for you. You can fine-tune the upload process
+    /// by adjusting the `chunk` size and the number of `concurrent` operations using [`Operator::writer_with`].
+    ///
+    /// # Examples
+    ///
+    /// Write data to a file only when it does not already exist:
+    ///
+    /// ```
+    /// # use opendal::Result;
+    /// # use opendal::Operator;
+    /// use opendal::options;
+    ///
+    /// # async fn test(op: Operator) -> Result<()> {
+    /// let _ = op
+    ///     .write_options("path/to/file", vec![0; 4096], options::WriteOptions {
+    ///         if_not_exists: true,
+    ///         ..Default::default()
+    ///     })
+    ///     .await?;
+    /// # Ok(())
+    /// # }
+    /// ```
     pub async fn write_options(
         &self,
         path: &str,
@@ -780,7 +826,7 @@ impl Operator {
         w.close().await
     }
 
-    /// Create a writer for streaming data to the given path.
+    /// Create a new writer of given path.
     ///
     /// # Notes
     ///
@@ -793,21 +839,8 @@ impl Operator {
     ///
     /// ## Extra Options
     ///
-    /// [`Operator::writer`] is a simplified version of [`Operator::writer_with`] without additional options.
-    /// For advanced features like `content_type` and `cache_control`, use [`Operator::writer_with`] instead.
-    ///
-    /// ## Chunk Size Handling
-    ///
-    /// Storage services often have specific requirements for chunk sizes:
-    /// - Services like `s3` may return `EntityTooSmall` errors for undersized chunks
-    /// - Using small chunks in cloud storage services can lead to increased costs
-    ///
-    /// OpenDAL automatically determines optimal chunk sizes based on the service's
-    /// [Capability](crate::types::Capability). However, you can override this by explicitly
-    /// setting the `chunk` parameter.
-    ///
-    /// For improved performance, consider setting an appropriate chunk size using
-    /// [`Operator::writer_with`].
+    /// [`Operator::writer`] is a simplified version that does not include additional options.
+    /// For advanced features such as `chunk` and `concurrent`, use [`Operator::writer_with`] or [`Operator::writer_options`] instead.
     ///
     /// # Examples
     ///
@@ -828,13 +861,30 @@ impl Operator {
         self.writer_with(path).await
     }
 
-    /// Create a writer for streaming data to the given path with more options.
+    /// Create a new writer of given path with additional options.
     ///
-    /// ## Options
+    /// # Notes
     ///
-    /// Visit [`options::WriteOptions`] for all available options.
+    /// ## Writer Features
+    ///
+    /// The writer provides several powerful capabilities:
+    /// - Streaming writes for continuous data transfer
+    /// - Automatic multipart upload handling
+    /// - Memory-efficient chunk-based writing
+    ///
+    /// ## Chunk Size Handling
     ///
-    /// ## Examples
+    /// Storage services often have specific requirements for chunk sizes:
+    /// - Services like `s3` may return `EntityTooSmall` errors for undersized chunks
+    /// - Using small chunks in cloud storage services can lead to increased costs
+    ///
+    /// OpenDAL automatically determines optimal chunk sizes based on the service's
+    /// [Capability](crate::types::Capability). However, you can override this by explicitly
+    /// setting the `chunk` parameter.
+    ///
+    /// Visit [`performance::concurrent_write`][crate::docs::performance::concurrent_write] for more details on concurrent writes.
+    ///
+    /// # Examples
     ///
     /// ```
     /// # use opendal::Result;
@@ -864,11 +914,50 @@ impl Operator {
         )
     }
 
-    /// Create a writer for streaming data to the given path with more options.
+    /// Create a new writer of given path with additional options.
     ///
-    /// ## Options
+    /// # Notes
     ///
-    /// Visit [`options::WriteOptions`] for all available options.
+    /// ## Writer Features
+    ///
+    /// The writer provides several powerful capabilities:
+    /// - Streaming writes for continuous data transfer
+    /// - Automatic multipart upload handling
+    /// - Memory-efficient chunk-based writing
+    ///
+    /// ## Chunk Size Handling
+    ///
+    /// Storage services often have specific requirements for chunk sizes:
+    /// - Services like `s3` may return `EntityTooSmall` errors for undersized chunks
+    /// - Using small chunks in cloud storage services can lead to increased costs
+    ///
+    /// OpenDAL automatically determines optimal chunk sizes based on the service's
+    /// [Capability](crate::types::Capability). However, you can override this by explicitly
+    /// setting the `chunk` parameter.
+    ///
+    /// Visit [`performance::concurrent_write`][crate::docs::performance::concurrent_write] for more details on concurrent writes.
+    ///
+    /// # Examples
+    ///
+    /// Write data to a file in 4MiB chunk size and at 8 concurrency:
+    ///
+    /// ```
+    /// # use opendal::Result;
+    /// # use opendal::Operator;
+    /// use bytes::Bytes;
+    ///
+    /// # async fn test(op: Operator) -> Result<()> {
+    /// let mut w = op
+    ///     .writer_with("path/to/file")
+    ///     .chunk(4 * 1024 * 1024)
+    ///     .concurrent(8)
+    ///     .await?;
+    /// w.write(vec![0; 4096]).await?;
+    /// w.write(vec![1; 4096]).await?;
+    /// w.close().await?;
+    /// # Ok(())
+    /// # }
+    /// ```
     pub async fn writer_options(&self, path: &str, opts: options::WriteOptions) -> Result<Writer> {
         let path = normalize_path(path);
         Self::writer_inner(self.inner().clone(), path, opts).await
@@ -1031,7 +1120,7 @@ impl Operator {
         self.delete_with(path).await
     }
 
-    /// Delete the given path with extra options.
+    /// Delete the given path with additional options.
     ///
     /// # Notes
     ///
@@ -1039,13 +1128,11 @@ impl Operator {
     ///
     /// # Options
     ///
-    /// ## `version`
-    ///
-    /// Set `version` for this `delete` request.
+    /// Visit [`options::DeleteOptions`] for all available options.
     ///
-    /// remove a specific version of the given path.
+    /// # Examples
     ///
-    /// If the version doesn't exist, OpenDAL will not return errors.
+    /// Delete a specific version of a file:
     ///
     /// ```
     /// # use opendal::Result;
@@ -1056,19 +1143,6 @@ impl Operator {
     /// # Ok(())
     /// # }
     /// ```
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use anyhow::Result;
-    /// # use futures::io;
-    /// # use opendal::Operator;
-    ///
-    /// # async fn test(op: Operator) -> Result<()> {
-    /// op.delete_with("test").await?;
-    /// # Ok(())
-    /// # }
-    /// ```
     pub fn delete_with(&self, path: &str) -> FutureDelete<impl Future<Output = Result<()>>> {
         let path = normalize_path(path);
 
@@ -1080,11 +1154,30 @@ impl Operator {
         )
     }
 
-    /// Delete the given path with extra options.
+    /// Delete the given path with additional options.
     ///
-    /// ## Options
+    /// # Notes
     ///
-    /// Visit [`options::DeleteOptions`] for all available options.
+    /// - Deleting a file that does not exist won't return errors.
+    ///
+    /// # Examples
+    ///
+    /// Delete a specific version of a file:
+    ///
+    /// ```
+    /// # use opendal::Result;
+    /// # use opendal::Operator;
+    /// use opendal::options;
+    ///
+    /// # async fn test(op: Operator, version: &str) -> Result<()> {
+    /// op.delete_with("path/to/file", options::DeleteOptions {
+    ///     version: Some(version.to_string()),
+    ///     ..Defeault::default()
+    /// })
+    /// .await?;
+    /// # Ok(())
+    /// # }
+    /// ```
     pub async fn delete_options(&self, path: &str, opts: options::DeleteOptions) -> Result<()> {
         let path = normalize_path(path);
         Self::delete_inner(self.inner().clone(), path, opts).await
@@ -1179,68 +1272,6 @@ impl Operator {
         Deleter::create(self.inner().clone()).await
     }
 
-    /// # Notes
-    ///
-    /// If underlying services support delete in batch, we will use batch
-    /// delete instead.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use anyhow::Result;
-    /// # use futures::io;
-    /// # use opendal::Operator;
-    /// #
-    /// # async fn test(op: Operator) -> Result<()> {
-    /// op.remove(vec!["abc".to_string(), "def".to_string()])
-    ///     .await?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    #[deprecated(note = "use `Operator::delete_iter` instead", since = "0.52.0")]
-    pub async fn remove(&self, paths: Vec<String>) -> Result<()> {
-        let mut deleter = self.deleter().await?;
-        deleter.delete_iter(paths).await?;
-        deleter.close().await?;
-        Ok(())
-    }
-
-    /// remove will remove files via the given paths.
-    ///
-    /// remove_via will remove files via the given stream.
-    ///
-    /// We will delete by chunks with given batch limit on the stream.
-    ///
-    /// # Notes
-    ///
-    /// If underlying services support delete in batch, we will use batch
-    /// delete instead.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # use anyhow::Result;
-    /// # use futures::io;
-    /// # use opendal::Operator;
-    /// use futures::stream;
-    /// #
-    /// # async fn test(op: Operator) -> Result<()> {
-    /// let stream = stream::iter(vec!["abc".to_string(), "def".to_string()]);
-    /// op.remove_via(stream).await?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    #[deprecated(note = "use `Operator::delete_stream` instead", since = "0.52.0")]
-    pub async fn remove_via(&self, input: impl Stream<Item = String> + Unpin) -> Result<()> {
-        let mut deleter = self.deleter().await?;
-        deleter
-            .delete_stream(input.map(|v| normalize_path(&v)))
-            .await?;
-        deleter.close().await?;
-
-        Ok(())
-    }
-
     /// Remove the path and all nested dirs and files recursively.
     ///
     /// # Notes
@@ -1284,29 +1315,23 @@ impl Operator {
         Ok(())
     }
 
-    /// List entries that starts with given `path` in parent dir.
+    /// List entries in the parent directory that start with the specified `path`.
     ///
     /// # Notes
     ///
     /// ## Recursively List
     ///
-    /// This function only read the children of the given directory. To read
-    /// all entries recursively, use `Operator::list_with("path").recursive(true)`
-    /// instead.
+    /// This function only reads the immediate children of the specified directory.
+    /// To list all entries recursively, use `Operator::list_with("path").recursive(true)` instead.
     ///
     /// ## Streaming List
     ///
-    /// This function will read all entries in the given directory. It could
-    /// take very long time and consume a lot of memory if the directory
-    /// contains a lot of entries.
+    /// This function reads all entries in the specified directory. If the directory contains many entries, this process may take a long time and use significant memory.
     ///
-    /// In order to avoid this, you can use [`Operator::lister`] to list entries in
-    /// a streaming way.
+    /// To prevent this, consider using [`Operator::lister`] to stream the entries instead.
     ///
     /// # Examples
     ///
-    /// ## List entries under a dir
-    ///
     /// This example will list all entries under the dir `path/to/dir/`.
     ///
     /// ```
@@ -1329,118 +1354,34 @@ impl Operator {
     /// # Ok(())
     /// # }
     /// ```
-    ///
-    /// ## List entries with prefix
-    ///
-    /// This example will list all entries under the prefix `path/to/prefix`.
-    ///
-    /// NOTE: it's possible that the prefix itself is also a dir. In this case, you could get
-    /// `path/to/prefix/`, `path/to/prefix_1` and so on. If you do want to list a dir, please
-    /// make sure the path is end with `/`.
-    ///
-    /// ```
-    /// # use anyhow::Result;
-    /// use opendal::EntryMode;
-    /// use opendal::Operator;
-    /// # async fn test(op: Operator) -> Result<()> {
-    /// let mut entries = op.list("path/to/prefix").await?;
-    /// for entry in entries {
-    ///     match entry.metadata().mode() {
-    ///         EntryMode::FILE => {
-    ///             println!("Handling file")
-    ///         }
-    ///         EntryMode::DIR => {
-    ///             println!("Handling dir {}", entry.path())
-    ///         }
-    ///         EntryMode::Unknown => continue,
-    ///     }
-    /// }
-    /// # Ok(())
-    /// # }
-    /// ```
     pub async fn list(&self, path: &str) -> Result<Vec<Entry>> {
         self.list_with(path).await
     }
 
-    /// List entries that starts with given `path` in parent dir with more options.
+    /// List entries in the parent directory that start with the specified `path` with additional options.
     ///
     /// # Notes
     ///
-    /// ## Streaming list
+    /// ## Streaming List
     ///
-    /// This function will read all entries in the given directory. It could
-    /// take very long time and consume a lot of memory if the directory
-    /// contains a lot of entries.
+    /// This function reads all entries in the specified directory. If the directory contains many entries, this process may take a long time and use significant memory.
     ///
-    /// In order to avoid this, you can use [`Operator::lister`] to list entries in
-    /// a streaming way.
+    /// To prevent this, consider using [`Operator::lister`] to stream the entries instead.
     ///
     /// # Options
     ///
-    /// ## `start_after`
-    ///
-    /// Specify the specified key to start listing from.
-    ///
-    /// This feature can be used to resume a listing from a previous point.
-    ///
-    /// The following example will resume the list operation from the `breakpoint`.
-    ///
-    /// ```
-    /// # use opendal::Result;
-    /// use opendal::Operator;
-    /// # async fn test(op: Operator) -> Result<()> {
-    /// let mut entries = op
-    ///     .list_with("path/to/dir/")
-    ///     .start_after("breakpoint")
-    ///     .await?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    ///
-    /// ## `recursive`
-    ///
-    /// Specify whether to list recursively or not.
-    ///
-    /// If `recursive` is set to `true`, we will list all entries recursively. If not, we'll only
-    /// list the entries in the specified dir.
-    ///
-    /// ```
-    /// # use opendal::Result;
-    /// use opendal::Operator;
-    /// # async fn test(op: Operator) -> Result<()> {
-    /// let mut entries = op.list_with("path/to/dir/").recursive(true).await?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    ///
-    /// ## `version`
-    ///
-    /// Specify whether to list files along with all their versions
-    ///
-    /// if `version` is enabled, all file versions will be returned; otherwise,
-    /// only the current files will be returned.
-    ///
-    /// ```
-    /// # use opendal::Result;
-    /// # use opendal::Operator;
-    /// # async fn test(op: Operator) -> Result<()> {
-    /// let mut entries = op.list_with("path/to/dir/").versions(true).await?;
-    /// # Ok(())
-    /// # }
-    /// ```
+    /// Visit [`options::ListOptions`] for all available options.
     ///
     /// # Examples
     ///
-    /// ## List all entries recursively
-    ///
-    /// This example will list all entries under the dir `path/to/dir/`
+    /// This example will list all entries recursively under the prefix `path/to/prefix`.
     ///
     /// ```
     /// # use anyhow::Result;
     /// use opendal::EntryMode;
     /// use opendal::Operator;
     /// # async fn test(op: Operator) -> Result<()> {
-    /// let mut entries = op.list_with("path/to/dir/").recursive(true).await?;
+    /// let mut entries = op.list_with("path/to/prefix").recursive(true).await?;
     /// for entry in entries {
     ///     match entry.metadata().mode() {
     ///         EntryMode::FILE => {
@@ -1455,17 +1396,47 @@ impl Operator {
     /// # Ok(())
     /// # }
     /// ```
+    pub fn list_with(&self, path: &str) -> FutureList<impl Future<Output = Result<Vec<Entry>>>> {
+        let path = normalize_path(path);
+
+        OperatorFuture::new(
+            self.inner().clone(),
+            path,
+            options::ListOptions::default(),
+            Self::list_inner,
+        )
+    }
+
+    /// List entries in the parent directory that start with the specified `path` with additional options.
+    ///
+    /// # Notes
+    ///
+    /// ## Streaming List
     ///
-    /// ## List all entries start with prefix
+    /// This function reads all entries in the specified directory. If the directory contains many entries, this process may take a long time and use significant memory.
+    ///
+    /// To prevent this, consider using [`Operator::lister`] to stream the entries instead.
+    ///
+    /// # Options
+    ///
+    /// Visit [`options::ListOptions`] for all available options.
+    ///
+    /// # Examples
     ///
-    /// This example will list all entries starts with prefix `path/to/prefix`
+    /// This example will list all entries recursively under the prefix `path/to/prefix`.
     ///
     /// ```
     /// # use anyhow::Result;
+    /// use opendal::options;
     /// use opendal::EntryMode;
     /// use opendal::Operator;
     /// # async fn test(op: Operator) -> Result<()> {
-    /// let mut entries = op.list_with("path/to/prefix").recursive(true).await?;
+    /// let mut entries = op
+    ///     .list_options("path/to/prefix", options::ListOptions {
+    ///         recursive: true,
+    ///         ..Default::default()
+    ///     })
+    ///     .await?;
     /// for entry in entries {
     ///     match entry.metadata().mode() {
     ///         EntryMode::FILE => {
@@ -1480,22 +1451,6 @@ impl Operator {
     /// # Ok(())
     /// # }
     /// ```
-    pub fn list_with(&self, path: &str) -> FutureList<impl Future<Output = Result<Vec<Entry>>>> {
-        let path = normalize_path(path);
-
-        OperatorFuture::new(
-            self.inner().clone(),
-            path,
-            options::ListOptions::default(),
-            Self::list_inner,
-        )
-    }
-
-    /// List entries that starts with given `path` in parent dir with more options.
-    ///
-    /// ## Options
-    ///
-    /// Visit [`options::ListOptions`] for all available options.
     pub async fn list_options(&self, path: &str, opts: options::ListOptions) -> Result<Vec<Entry>> {
         let path = normalize_path(path);
         Self::list_inner(self.inner().clone(), path, opts).await
@@ -1512,18 +1467,14 @@ impl Operator {
         lister.try_collect().await
     }
 
-    /// List entries that starts with given `path` in parent dir.
-    ///
-    /// This function will create a new [`Lister`] to list entries. Users can stop
-    /// listing via dropping this [`Lister`].
+    /// Create a new lister to list entries that starts with given `path` in parent dir.
     ///
     /// # Notes
     ///
     /// ## Recursively list
     ///
-    /// This function only read the children of the given directory. To read
-    /// all entries recursively, use [`Operator::lister_with`] and `recursive(true)`
-    /// instead.
+    /// This function only reads the immediate children of the specified directory.
+    /// To retrieve all entries recursively, use [`Operator::lister_with`] with `recursive(true)` instead.
     ///
     /// # Examples
     ///
@@ -1553,64 +1504,11 @@ impl Operator {
         self.lister_with(path).await
     }
 
-    /// List entries that starts with given `path` in parent dir with options.
-    ///
-    /// This function will create a new [`Lister`] to list entries. Users can stop listing via
-    /// dropping this [`Lister`].
+    /// Create a new lister to list entries that starts with given `path` in parent dir with additional options.
     ///
     /// # Options
     ///
-    /// ## `start_after`
-    ///
-    /// Specify the specified key to start listing from.
-    ///
-    /// This feature can be used to resume a listing from a previous point.
-    ///
-    /// The following example will resume the list operation from the `breakpoint`.
-    ///
-    /// ```
-    /// # use opendal::Result;
-    /// use opendal::Operator;
-    /// # async fn test(op: Operator) -> Result<()> {
-    /// let mut lister = op
-    ///     .lister_with("path/to/dir/")
-    ///     .start_after("breakpoint")
-    ///     .await?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    ///
-    /// ## `recursive`
-    ///
-    /// Specify whether to list recursively or not.
-    ///
-    /// If `recursive` is set to `true`, we will list all entries recursively. If not, we'll only
-    /// list the entries in the specified dir.
-    ///
-    /// ```
-    /// # use opendal::Result;
-    /// use opendal::Operator;
-    /// # async fn test(op: Operator) -> Result<()> {
-    /// let mut lister = op.lister_with("path/to/dir/").recursive(true).await?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    ///
-    /// ## `version`
-    ///
-    /// Specify whether to list files along with all their versions
-    ///
-    /// if `version` is enabled, all file versions will be returned; otherwise,
-    /// only the current files will be returned.
-    ///
-    /// ```
-    /// # use opendal::Result;
-    /// # use opendal::Operator;
-    /// # async fn test(op: Operator) -> Result<()> {
-    /// let mut entries = op.lister_with("path/to/dir/").versions(true).await?;
-    /// # Ok(())
-    /// # }
-    /// ```
+    /// Visit [`options::ListOptions`] for all available options.
     ///
     /// # Examples
     ///
@@ -1648,14 +1546,39 @@ impl Operator {
         )
     }
 
-    /// List entries that starts with given `path` in parent dir with options.
+    /// Create a new lister to list entries that starts with given `path` in parent dir with additional options.
     ///
-    /// This function will create a new [`Lister`] to list entries. Users can stop listing via
-    /// dropping this [`Lister`].
+    /// # Examples
     ///
-    /// ## Options
+    /// ## List all files recursively
     ///
-    /// Visit [`options::ListOptions`] for all available options.
+    /// ```
+    /// # use anyhow::Result;
+    /// use futures::TryStreamExt;
+    /// use opendal::options;
+    /// use opendal::EntryMode;
+    /// use opendal::Operator;
+    /// # async fn test(op: Operator) -> Result<()> {
+    /// let mut lister = op
+    ///     .lister_options("path/to/dir/", options::ListOptions {
+    ///         recursive: true,
+    ///         ..Default::default()
+    ///     })
+    ///     .await?;
+    /// while let Some(mut entry) = lister.try_next().await? {
+    ///     match entry.metadata().mode() {
+    ///         EntryMode::FILE => {
+    ///             println!("Handling file {}", entry.path())
+    ///         }
+    ///         EntryMode::DIR => {
+    ///             println!("Handling dir {}", entry.path())
+    ///         }
+    ///         EntryMode::Unknown => continue,
+    ///     }
+    /// }
+    /// # Ok(())
+    /// # }
+    /// ```
     pub async fn lister_options(&self, path: &str, opts: options::ListOptions) -> Result<Lister> {
         let path = normalize_path(path);
         Self::lister_inner(self.inner().clone(), path, opts).await