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}