opendal_core/blocking/
operator.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::time::Duration;
19
20use tokio::runtime::Handle;
21
22use crate::Operator as AsyncOperator;
23use crate::raw::PresignedRequest;
24use crate::types::IntoOperatorUri;
25use crate::*;
26
27/// Use OpenDAL in blocking context.
28///
29/// # Notes
30///
31/// blocking::Operator is a wrapper around [`AsyncOperator`]. It calls async runtimes' `block_on` API to spawn blocking tasks.
32/// Please avoid using blocking::Operator in async context.
33///
34/// # Examples
35///
36/// ## Init in async context
37///
38/// blocking::Operator will use current async context's runtime to handle the async calls.
39///
40/// This is just for initialization. You must use `blocking::Operator` in blocking context.
41///
42/// ```rust,no_run
43/// # use opendal_core::services;
44/// # use opendal_core::blocking;
45/// # use opendal_core::Operator;
46/// # use opendal_core::Result;
47///
48/// #[tokio::main]
49/// async fn main() -> Result<()> {
50///     // Create fs backend builder.
51///     let builder = services::Memory::default();
52///     let op = Operator::new(builder)?.finish();
53///
54///     // Build an `blocking::Operator` with blocking layer to start operating the storage.
55///     let _: blocking::Operator = blocking::Operator::new(op)?;
56///
57///     Ok(())
58/// }
59/// ```
60///
61/// ## In async context with blocking functions
62///
63/// If `blocking::Operator` is called in blocking function, please fetch a [`tokio::runtime::EnterGuard`]
64/// first. You can use [`Handle::try_current`] first to get the handle and then call [`Handle::enter`].
65/// This often happens in the case that async function calls blocking function.
66///
67/// ```rust,no_run
68/// # use opendal_core::services;
69/// # use opendal_core::blocking;
70/// # use opendal_core::Operator;
71/// # use opendal_core::Result;
72///
73/// #[tokio::main]
74/// async fn main() -> Result<()> {
75///     let _ = blocking_fn()?;
76///     Ok(())
77/// }
78///
79/// fn blocking_fn() -> Result<blocking::Operator> {
80///     // Create fs backend builder.
81///     let builder = services::Memory::default();
82///     let op = Operator::new(builder)?.finish();
83///
84///     let handle = tokio::runtime::Handle::try_current().unwrap();
85///     let _guard = handle.enter();
86///     // Build an `blocking::Operator` to start operating the storage.
87///     let op: blocking::Operator = blocking::Operator::new(op)?;
88///     Ok(op)
89/// }
90/// ```
91///
92/// ## In blocking context
93///
94/// In a pure blocking context, we can create a runtime and use it to create the `blocking::Operator`.
95///
96/// > The following code uses a global statically created runtime as an example, please manage the
97/// > runtime on demand.
98///
99/// ```rust,no_run
100/// # use std::sync::LazyLock;
101/// # use opendal_core::services;
102/// # use opendal_core::blocking;
103/// # use opendal_core::Operator;
104/// # use opendal_core::Result;
105///
106/// static RUNTIME: LazyLock<tokio::runtime::Runtime> = LazyLock::new(|| {
107///     tokio::runtime::Builder::new_multi_thread()
108///         .enable_all()
109///         .build()
110///         .unwrap()
111/// });
112///
113/// fn main() -> Result<()> {
114///     // Create fs backend builder.
115///     let builder = services::Memory::default();
116///     let op = Operator::new(builder)?.finish();
117///
118///     // Fetch the `EnterGuard` from global runtime.
119///     let _guard = RUNTIME.enter();
120///     // Build an `blocking::Operator` with blocking layer to start operating the storage.
121///     let _: blocking::Operator = blocking::Operator::new(op)?;
122///
123///     Ok(())
124/// }
125/// ```
126#[derive(Clone, Debug)]
127pub struct Operator {
128    handle: tokio::runtime::Handle,
129    op: AsyncOperator,
130}
131
132impl Operator {
133    /// Create a new `BlockingLayer` with the current runtime's handle
134    pub fn new(op: AsyncOperator) -> Result<Self> {
135        Ok(Self {
136            handle: Handle::try_current()
137                .map_err(|_| Error::new(ErrorKind::Unexpected, "failed to get current handle"))?,
138            op,
139        })
140    }
141
142    /// Create a blocking operator from URI based configuration.
143    pub fn from_uri(uri: impl IntoOperatorUri) -> Result<Self> {
144        let op = AsyncOperator::from_uri(uri)?;
145        Self::new(op)
146    }
147
148    /// Get information of underlying accessor.
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// # use std::sync::Arc;
154    /// use opendal_core::blocking;
155    /// # use anyhow::Result;
156    /// use opendal_core::blocking::Operator;
157    ///
158    /// # fn test(op: blocking::Operator) -> Result<()> {
159    /// let info = op.info();
160    /// # Ok(())
161    /// # }
162    /// ```
163    pub fn info(&self) -> OperatorInfo {
164        self.op.info()
165    }
166}
167
168/// # Operator blocking API.
169impl Operator {
170    /// Create a presigned request for stat.
171    ///
172    /// See [`Operator::presign_stat`] for more details.
173    pub fn presign_stat(&self, path: &str, expire: Duration) -> Result<PresignedRequest> {
174        self.handle.block_on(self.op.presign_stat(path, expire))
175    }
176
177    /// Create a presigned request for read.
178    ///
179    /// See [`Operator::presign_read`] for more details.
180    pub fn presign_read(&self, path: &str, expire: Duration) -> Result<PresignedRequest> {
181        self.handle.block_on(self.op.presign_read(path, expire))
182    }
183
184    /// Create a presigned request for write.
185    ///
186    /// See [`Operator::presign_write`] for more details.
187    pub fn presign_write(&self, path: &str, expire: Duration) -> Result<PresignedRequest> {
188        self.handle.block_on(self.op.presign_write(path, expire))
189    }
190
191    /// Create a presigned request for delete.
192    ///
193    /// See [`Operator::presign_delete`] for more details.
194    pub fn presign_delete(&self, path: &str, expire: Duration) -> Result<PresignedRequest> {
195        self.handle.block_on(self.op.presign_delete(path, expire))
196    }
197
198    /// Get given path's metadata.
199    ///
200    /// # Behavior
201    ///
202    /// ## Services that support `create_dir`
203    ///
204    /// `test` and `test/` may vary in some services such as S3. However, on a local file system,
205    /// they're identical. Therefore, the behavior of `stat("test")` and `stat("test/")` might differ
206    /// in certain edge cases. Always use `stat("test/")` when you need to access a directory if possible.
207    ///
208    /// Here are the behavior list:
209    ///
210    /// | Case                   | Path            | Result                                     |
211    /// |------------------------|-----------------|--------------------------------------------|
212    /// | stat existing dir      | `abc/`          | Metadata with dir mode                     |
213    /// | stat existing file     | `abc/def_file`  | Metadata with file mode                    |
214    /// | stat dir without `/`   | `abc/def_dir`   | Error `NotFound` or metadata with dir mode |
215    /// | stat file with `/`     | `abc/def_file/` | Error `NotFound`                           |
216    /// | stat not existing path | `xyz`           | Error `NotFound`                           |
217    ///
218    /// Refer to [RFC: List Prefix][crate::docs::rfcs::rfc_3243_list_prefix] for more details.
219    ///
220    /// ## Services that not support `create_dir`
221    ///
222    /// For services that not support `create_dir`, `stat("test/")` will return `NotFound` even
223    /// when `test/abc` exists since the service won't have the concept of dir. There is nothing
224    /// we can do about this.
225    ///
226    /// # Examples
227    ///
228    /// ## Check if file exists
229    ///
230    /// ```
231    /// # use anyhow::Result;
232    /// # use futures::io;
233    /// use opendal_core::blocking;
234    /// # use opendal_core::blocking::Operator;
235    /// use opendal_core::ErrorKind;
236    /// #
237    /// # fn test(op: blocking::Operator) -> Result<()> {
238    /// if let Err(e) = op.stat("test") {
239    ///     if e.kind() == ErrorKind::NotFound {
240    ///         println!("file not exist")
241    ///     }
242    /// }
243    /// # Ok(())
244    /// # }
245    /// ```
246    pub fn stat(&self, path: &str) -> Result<Metadata> {
247        self.stat_options(path, options::StatOptions::default())
248    }
249
250    /// Get given path's metadata with extra options.
251    ///
252    /// # Behavior
253    ///
254    /// ## Services that support `create_dir`
255    ///
256    /// `test` and `test/` may vary in some services such as S3. However, on a local file system,
257    /// they're identical. Therefore, the behavior of `stat("test")` and `stat("test/")` might differ
258    /// in certain edge cases. Always use `stat("test/")` when you need to access a directory if possible.
259    ///
260    /// Here are the behavior list:
261    ///
262    /// | Case                   | Path            | Result                                     |
263    /// |------------------------|-----------------|--------------------------------------------|
264    /// | stat existing dir      | `abc/`          | Metadata with dir mode                     |
265    /// | stat existing file     | `abc/def_file`  | Metadata with file mode                    |
266    /// | stat dir without `/`   | `abc/def_dir`   | Error `NotFound` or metadata with dir mode |
267    /// | stat file with `/`     | `abc/def_file/` | Error `NotFound`                           |
268    /// | stat not existing path | `xyz`           | Error `NotFound`                           |
269    ///
270    /// Refer to [RFC: List Prefix][crate::docs::rfcs::rfc_3243_list_prefix] for more details.
271    ///
272    /// ## Services that not support `create_dir`
273    ///
274    /// For services that not support `create_dir`, `stat("test/")` will return `NotFound` even
275    /// when `test/abc` exists since the service won't have the concept of dir. There is nothing
276    /// we can do about this.
277    pub fn stat_options(&self, path: &str, opts: options::StatOptions) -> Result<Metadata> {
278        self.handle.block_on(self.op.stat_options(path, opts))
279    }
280
281    /// Check if this path exists or not.
282    ///
283    /// # Example
284    ///
285    /// ```no_run
286    /// use anyhow::Result;
287    /// use opendal_core::blocking;
288    /// use opendal_core::blocking::Operator;
289    /// fn test(op: blocking::Operator) -> Result<()> {
290    ///     let _ = op.exists("test")?;
291    ///
292    ///     Ok(())
293    /// }
294    /// ```
295    pub fn exists(&self, path: &str) -> Result<bool> {
296        let r = self.stat(path);
297        match r {
298            Ok(_) => Ok(true),
299            Err(err) => match err.kind() {
300                ErrorKind::NotFound => Ok(false),
301                _ => Err(err),
302            },
303        }
304    }
305
306    /// Create a dir at given path.
307    ///
308    /// # Notes
309    ///
310    /// To indicate that a path is a directory, it is compulsory to include
311    /// a trailing / in the path. Failure to do so may result in
312    /// `NotADirectory` error being returned by OpenDAL.
313    ///
314    /// # Behavior
315    ///
316    /// - Create on existing dir will succeed.
317    /// - Create dir is always recursive, works like `mkdir -p`
318    ///
319    /// # Examples
320    ///
321    /// ```no_run
322    /// # use opendal_core::Result;
323    /// use opendal_core::blocking;
324    /// # use opendal_core::blocking::Operator;
325    /// # use futures::TryStreamExt;
326    /// # fn test(op: blocking::Operator) -> Result<()> {
327    /// op.create_dir("path/to/dir/")?;
328    /// # Ok(())
329    /// # }
330    /// ```
331    pub fn create_dir(&self, path: &str) -> Result<()> {
332        self.handle.block_on(self.op.create_dir(path))
333    }
334
335    /// Read the whole path into a bytes.
336    ///
337    /// This function will allocate a new bytes internally. For more precise memory control or
338    /// reading data lazily, please use [`blocking::Operator::reader`]
339    ///
340    /// # Examples
341    ///
342    /// ```no_run
343    /// # use opendal_core::Result;
344    /// use opendal_core::blocking;
345    /// # use opendal_core::blocking::Operator;
346    /// #
347    /// # fn test(op: blocking::Operator) -> Result<()> {
348    /// let bs = op.read("path/to/file")?;
349    /// # Ok(())
350    /// # }
351    /// ```
352    pub fn read(&self, path: &str) -> Result<Buffer> {
353        self.read_options(path, options::ReadOptions::default())
354    }
355
356    /// Read the whole path into a bytes with extra options.
357    ///
358    /// This function will allocate a new bytes internally. For more precise memory control or
359    /// reading data lazily, please use [`blocking::Operator::reader`]
360    pub fn read_options(&self, path: &str, opts: options::ReadOptions) -> Result<Buffer> {
361        self.handle.block_on(self.op.read_options(path, opts))
362    }
363
364    /// Create a new reader which can read the whole path.
365    ///
366    /// # Examples
367    ///
368    /// ```no_run
369    /// # use opendal_core::Result;
370    /// use opendal_core::blocking;
371    /// # use opendal_core::blocking::Operator;
372    /// # use futures::TryStreamExt;
373    /// # fn test(op: blocking::Operator) -> Result<()> {
374    /// let r = op.reader("path/to/file")?;
375    /// # Ok(())
376    /// # }
377    /// ```
378    pub fn reader(&self, path: &str) -> Result<blocking::Reader> {
379        self.reader_options(path, options::ReaderOptions::default())
380    }
381
382    /// Create a new reader with extra options
383    pub fn reader_options(
384        &self,
385        path: &str,
386        opts: options::ReaderOptions,
387    ) -> Result<blocking::Reader> {
388        let r = self.handle.block_on(self.op.reader_options(path, opts))?;
389        Ok(blocking::Reader::new(self.handle.clone(), r))
390    }
391
392    /// Write bytes into given path.
393    ///
394    /// # Notes
395    ///
396    /// - Write will make sure all bytes has been written, or an error will be returned.
397    ///
398    /// # Examples
399    ///
400    /// ```no_run
401    /// # use opendal_core::Result;
402    /// # use opendal_core::blocking::Operator;
403    /// # use futures::StreamExt;
404    /// # use futures::SinkExt;
405    /// use bytes::Bytes;
406    /// use opendal_core::blocking;
407    ///
408    /// # fn test(op: blocking::Operator) -> Result<()> {
409    /// op.write("path/to/file", vec![0; 4096])?;
410    /// # Ok(())
411    /// # }
412    /// ```
413    pub fn write(&self, path: &str, bs: impl Into<Buffer>) -> Result<Metadata> {
414        self.write_options(path, bs, options::WriteOptions::default())
415    }
416
417    /// Write data with options.
418    ///
419    /// # Notes
420    ///
421    /// - Write will make sure all bytes has been written, or an error will be returned.
422    pub fn write_options(
423        &self,
424        path: &str,
425        bs: impl Into<Buffer>,
426        opts: options::WriteOptions,
427    ) -> Result<Metadata> {
428        self.handle.block_on(self.op.write_options(path, bs, opts))
429    }
430
431    /// Write multiple bytes into given path.
432    ///
433    /// # Notes
434    ///
435    /// - Write will make sure all bytes has been written, or an error will be returned.
436    ///
437    /// # Examples
438    ///
439    /// ```no_run
440    /// # use opendal_core::Result;
441    /// # use opendal_core::blocking;
442    /// # use opendal_core::blocking::Operator;
443    /// # use futures::StreamExt;
444    /// # use futures::SinkExt;
445    /// use bytes::Bytes;
446    ///
447    /// # fn test(op: blocking::Operator) -> Result<()> {
448    /// let mut w = op.writer("path/to/file")?;
449    /// w.write(vec![0; 4096])?;
450    /// w.write(vec![1; 4096])?;
451    /// w.close()?;
452    /// # Ok(())
453    /// # }
454    /// ```
455    pub fn writer(&self, path: &str) -> Result<blocking::Writer> {
456        self.writer_options(path, options::WriteOptions::default())
457    }
458
459    /// Create a new writer with extra options
460    pub fn writer_options(
461        &self,
462        path: &str,
463        opts: options::WriteOptions,
464    ) -> Result<blocking::Writer> {
465        let w = self.handle.block_on(self.op.writer_options(path, opts))?;
466        Ok(blocking::Writer::new(self.handle.clone(), w))
467    }
468
469    /// Copy a file from `from` to `to`.
470    ///
471    /// # Notes
472    ///
473    /// - `from` and `to` must be a file.
474    /// - `to` will be overwritten if it exists.
475    /// - If `from` and `to` are the same, nothing will happen.
476    /// - `copy` is idempotent. For same `from` and `to` input, the result will be the same.
477    ///
478    /// # Examples
479    ///
480    /// ```
481    /// # use opendal_core::Result;
482    /// use opendal_core::blocking;
483    /// # use opendal_core::blocking::Operator;
484    ///
485    /// # fn test(op: blocking::Operator) -> Result<()> {
486    /// op.copy("path/to/file", "path/to/file2")?;
487    /// # Ok(())
488    /// # }
489    /// ```
490    pub fn copy(&self, from: &str, to: &str) -> Result<()> {
491        self.handle.block_on(self.op.copy(from, to))
492    }
493
494    /// Rename a file from `from` to `to`.
495    ///
496    /// # Notes
497    ///
498    /// - `from` and `to` must be a file.
499    /// - `to` will be overwritten if it exists.
500    /// - If `from` and `to` are the same, a `IsSameFile` error will occur.
501    ///
502    /// # Examples
503    ///
504    /// ```
505    /// # use opendal_core::Result;
506    /// use opendal_core::blocking;
507    /// # use opendal_core::blocking::Operator;
508    ///
509    /// # fn test(op: blocking::Operator) -> Result<()> {
510    /// op.rename("path/to/file", "path/to/file2")?;
511    /// # Ok(())
512    /// # }
513    /// ```
514    pub fn rename(&self, from: &str, to: &str) -> Result<()> {
515        self.handle.block_on(self.op.rename(from, to))
516    }
517
518    /// Delete given path.
519    ///
520    /// # Notes
521    ///
522    /// - Delete not existing error won't return errors.
523    ///
524    /// # Examples
525    ///
526    /// ```no_run
527    /// # use anyhow::Result;
528    /// # use futures::io;
529    /// use opendal_core::blocking;
530    /// # use opendal_core::blocking::Operator;
531    /// # fn test(op: blocking::Operator) -> Result<()> {
532    /// op.delete("path/to/file")?;
533    /// # Ok(())
534    /// # }
535    /// ```
536    pub fn delete(&self, path: &str) -> Result<()> {
537        self.delete_options(path, options::DeleteOptions::default())
538    }
539
540    /// Delete given path with options.
541    ///
542    /// # Notes
543    ///
544    /// - Delete not existing error won't return errors.
545    pub fn delete_options(&self, path: &str, opts: options::DeleteOptions) -> Result<()> {
546        self.handle.block_on(self.op.delete_options(path, opts))
547    }
548
549    /// Delete an infallible iterator of paths.
550    ///
551    /// Also see:
552    ///
553    /// - [`blocking::Operator::delete_try_iter`]: delete an fallible iterator of paths.
554    pub fn delete_iter<I, D>(&self, iter: I) -> Result<()>
555    where
556        I: IntoIterator<Item = D>,
557        D: IntoDeleteInput,
558    {
559        self.handle.block_on(self.op.delete_iter(iter))
560    }
561
562    /// Delete a fallible iterator of paths.
563    ///
564    /// Also see:
565    ///
566    /// - [`blocking::Operator::delete_iter`]: delete an infallible iterator of paths.
567    pub fn delete_try_iter<I, D>(&self, try_iter: I) -> Result<()>
568    where
569        I: IntoIterator<Item = Result<D>>,
570        D: IntoDeleteInput,
571    {
572        self.handle.block_on(self.op.delete_try_iter(try_iter))
573    }
574
575    /// Create a [`BlockingDeleter`] to continuously remove content from storage.
576    ///
577    /// It leverages batch deletion capabilities provided by storage services for efficient removal.
578    ///
579    /// Users can have more control over the deletion process by using [`BlockingDeleter`] directly.
580    pub fn deleter(&self) -> Result<blocking::Deleter> {
581        blocking::Deleter::create(
582            self.handle.clone(),
583            self.handle.block_on(self.op.deleter())?,
584        )
585    }
586
587    /// Remove the path and all nested dirs and files recursively.
588    ///
589    /// # Deprecated
590    ///
591    /// This method is deprecated since v0.55.0. Use [`blocking::Operator::delete_options`] with
592    /// `recursive: true` instead.
593    ///
594    /// ## Migration Example
595    ///
596    /// Instead of:
597    /// ```ignore
598    /// op.remove_all("path/to/dir")?;
599    /// ```
600    ///
601    /// Use:
602    /// ```ignore
603    /// use opendal_core::options::DeleteOptions;
604    /// op.delete_options("path/to/dir", DeleteOptions {
605    ///     recursive: true,
606    ///     ..Default::default()
607    /// })?;
608    /// ```
609    ///
610    /// # Notes
611    ///
612    /// If underlying services support delete in batch, we will use batch
613    /// delete instead.
614    ///
615    /// # Examples
616    ///
617    /// ```
618    /// # use anyhow::Result;
619    /// # use futures::io;
620    /// use opendal_core::blocking;
621    /// # use opendal_core::blocking::Operator;
622    /// # fn test(op: blocking::Operator) -> Result<()> {
623    /// op.remove_all("path/to/dir")?;
624    /// # Ok(())
625    /// # }
626    /// ```
627    #[deprecated(
628        since = "0.55.0",
629        note = "Use `delete_options` with `recursive: true` instead"
630    )]
631    #[allow(deprecated)]
632    pub fn remove_all(&self, path: &str) -> Result<()> {
633        self.delete_options(
634            path,
635            options::DeleteOptions {
636                recursive: true,
637                ..Default::default()
638            },
639        )
640    }
641
642    /// List entries whose paths start with the given prefix `path`.
643    ///
644    /// # Semantics
645    ///
646    /// - Listing is **prefix-based**; it doesn't require the parent directory to exist.
647    /// - If `path` itself exists, it is returned as an entry along with prefixed children.
648    /// - If `path` is missing but deeper objects exist, the list succeeds and returns those prefixed entries instead of an error.
649    /// - Set `recursive` in [`options::ListOptions`] via [`list_options`](Self::list_options) to walk all descendants; the default returns only immediate children when delimiter is supported.
650    ///
651    /// ## Streaming List
652    ///
653    /// This function materializes the full result in memory. For large listings, prefer [`blocking::Operator::lister`] to stream entries.
654    ///
655    /// # Examples
656    ///
657    /// ```no_run
658    /// # use anyhow::Result;
659    /// use opendal_core::blocking;
660    /// use opendal_core::blocking::Operator;
661    /// use opendal_core::EntryMode;
662    /// #  fn test(op: blocking::Operator) -> Result<()> {
663    /// let mut entries = op.list("path/to/dir/")?;
664    /// for entry in entries {
665    ///     match entry.metadata().mode() {
666    ///         EntryMode::FILE => {
667    ///             println!("Handling file")
668    ///         }
669    ///         EntryMode::DIR => {
670    ///             println!("Handling dir {}", entry.path())
671    ///         }
672    ///         EntryMode::Unknown => continue,
673    ///     }
674    /// }
675    /// # Ok(())
676    /// # }
677    /// ```
678    pub fn list(&self, path: &str) -> Result<Vec<Entry>> {
679        self.list_options(path, options::ListOptions::default())
680    }
681
682    /// List entries whose paths start with the given prefix `path` with additional options.
683    ///
684    /// # Semantics
685    ///
686    /// Inherits the prefix semantics described in [`Operator::list`] (blocking variant). It returns `path` itself if present and tolerates missing parents when prefixed objects exist.
687    ///
688    /// ## Streaming List
689    ///
690    /// This function materializes the full result in memory. For large listings, prefer [`blocking::Operator::lister`] to stream entries.
691    ///
692    /// ## Options
693    ///
694    /// See [`options::ListOptions`] for the full set. Common knobs: traversal (`recursive`), pagination (`limit`, `start_after`), and versioning (`versions`, `deleted`).
695    pub fn list_options(&self, path: &str, opts: options::ListOptions) -> Result<Vec<Entry>> {
696        self.handle.block_on(self.op.list_options(path, opts))
697    }
698
699    /// Create a streaming lister for entries whose paths start with the given prefix `path`.
700    ///
701    /// This function creates a new [`BlockingLister`]; dropping it stops listing.
702    ///
703    /// # Semantics
704    ///
705    /// Shares the same prefix semantics as [`blocking::Operator::list`]: parent directory is optional; `path` itself is yielded if present; missing parents with deeper objects are accepted.
706    ///
707    /// ## Options
708    ///
709    /// Accepts the same [`options::ListOptions`] as [`list_options`](Self::list_options): traversal (`recursive`), pagination (`limit`, `start_after`), and versioning (`versions`, `deleted`).
710    ///
711    /// # Examples
712    ///
713    /// ```no_run
714    /// # use anyhow::Result;
715    /// # use futures::io;
716    /// use futures::TryStreamExt;
717    /// use opendal_core::blocking;
718    /// use opendal_core::blocking::Operator;
719    /// use opendal_core::EntryMode;
720    /// # fn test(op: blocking::Operator) -> Result<()> {
721    /// let mut ds = op.lister("path/to/dir/")?;
722    /// for de in ds {
723    ///     let de = de?;
724    ///     match de.metadata().mode() {
725    ///         EntryMode::FILE => {
726    ///             println!("Handling file")
727    ///         }
728    ///         EntryMode::DIR => {
729    ///             println!("Handling dir like start a new list via meta.path()")
730    ///         }
731    ///         EntryMode::Unknown => continue,
732    ///     }
733    /// }
734    /// # Ok(())
735    /// # }
736    /// ```
737    pub fn lister(&self, path: &str) -> Result<blocking::Lister> {
738        self.lister_options(path, options::ListOptions::default())
739    }
740
741    /// List entries under a prefix as an iterator with options.
742    ///
743    /// This function creates a new handle to stream entries and inherits the prefix semantics of [`blocking::Operator::list`].
744    ///
745    /// ## Options
746    ///
747    /// Same as [`lister`](Self::lister); see [`options::ListOptions`] for traversal, pagination, and versioning knobs.
748    pub fn lister_options(
749        &self,
750        path: &str,
751        opts: options::ListOptions,
752    ) -> Result<blocking::Lister> {
753        let l = self.handle.block_on(self.op.lister_options(path, opts))?;
754        Ok(blocking::Lister::new(self.handle.clone(), l))
755    }
756
757    /// Check if this operator can work correctly.
758    ///
759    /// We will send a `list` request to path and return any errors we met.
760    ///
761    /// ```
762    /// # use std::sync::Arc;
763    /// # use anyhow::Result;
764    /// use opendal_core::blocking;
765    /// use opendal_core::blocking::Operator;
766    /// use opendal_core::ErrorKind;
767    ///
768    /// # fn test(op: blocking::Operator) -> Result<()> {
769    /// op.check()?;
770    /// # Ok(())
771    /// # }
772    /// ```
773    pub fn check(&self) -> Result<()> {
774        let mut ds = self.lister("/")?;
775
776        match ds.next() {
777            Some(Err(e)) if e.kind() != ErrorKind::NotFound => Err(e),
778            _ => Ok(()),
779        }
780    }
781}
782
783impl From<Operator> for AsyncOperator {
784    fn from(val: Operator) -> Self {
785        val.op
786    }
787}