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 /// Spawn a future onto the runtime's worker pool and block until it
143 /// completes.
144 ///
145 /// Unlike [`Handle::block_on`] which polls the future on the **calling**
146 /// thread's stack, this method runs the future on a tokio worker thread
147 /// (typically 8 MB stack) and only uses the calling thread to wait for
148 /// the result. This avoids stack overflows when the async state machine
149 /// is deeply nested (e.g. HF/XET uploads driven from a JVM thread with
150 /// a 1 MB default stack).
151 fn spawn_block<F>(&self, f: F) -> Result<F::Output>
152 where
153 F: std::future::Future + Send + 'static,
154 F::Output: Send + 'static,
155 {
156 self.handle.block_on(self.handle.spawn(f)).map_err(|err| {
157 Error::new(ErrorKind::Unexpected, "blocking task failed").set_source(err)
158 })
159 }
160
161 /// Create a blocking operator from URI based configuration.
162 pub fn from_uri(uri: impl IntoOperatorUri) -> Result<Self> {
163 let op = AsyncOperator::from_uri(uri)?;
164 Self::new(op)
165 }
166
167 /// Get information of underlying accessor.
168 ///
169 /// # Examples
170 ///
171 /// ```
172 /// # use std::sync::Arc;
173 /// use opendal_core::blocking;
174 /// # use anyhow::Result;
175 /// use opendal_core::blocking::Operator;
176 ///
177 /// # fn test(op: blocking::Operator) -> Result<()> {
178 /// let info = op.info();
179 /// # Ok(())
180 /// # }
181 /// ```
182 pub fn info(&self) -> OperatorInfo {
183 self.op.info()
184 }
185}
186
187/// # Operator blocking API.
188impl Operator {
189 /// Create a presigned request for stat.
190 ///
191 /// See [`Operator::presign_stat`] for more details.
192 pub fn presign_stat(&self, path: &str, expire: Duration) -> Result<PresignedRequest> {
193 self.handle.block_on(self.op.presign_stat(path, expire))
194 }
195
196 /// Create a presigned request for read.
197 ///
198 /// See [`Operator::presign_read`] for more details.
199 pub fn presign_read(&self, path: &str, expire: Duration) -> Result<PresignedRequest> {
200 self.handle.block_on(self.op.presign_read(path, expire))
201 }
202
203 /// Create a presigned request for write.
204 ///
205 /// See [`Operator::presign_write`] for more details.
206 pub fn presign_write(&self, path: &str, expire: Duration) -> Result<PresignedRequest> {
207 self.handle.block_on(self.op.presign_write(path, expire))
208 }
209
210 /// Create a presigned request for delete.
211 ///
212 /// See [`Operator::presign_delete`] for more details.
213 pub fn presign_delete(&self, path: &str, expire: Duration) -> Result<PresignedRequest> {
214 self.handle.block_on(self.op.presign_delete(path, expire))
215 }
216
217 /// Get given path's metadata.
218 ///
219 /// # Behavior
220 ///
221 /// ## Services that support `create_dir`
222 ///
223 /// `test` and `test/` may vary in some services such as S3. However, on a local file system,
224 /// they're identical. Therefore, the behavior of `stat("test")` and `stat("test/")` might differ
225 /// in certain edge cases. Always use `stat("test/")` when you need to access a directory if possible.
226 ///
227 /// Here are the behavior list:
228 ///
229 /// | Case | Path | Result |
230 /// |------------------------|-----------------|--------------------------------------------|
231 /// | stat existing dir | `abc/` | Metadata with dir mode |
232 /// | stat existing file | `abc/def_file` | Metadata with file mode |
233 /// | stat dir without `/` | `abc/def_dir` | Error `NotFound` or metadata with dir mode |
234 /// | stat file with `/` | `abc/def_file/` | Error `NotFound` |
235 /// | stat not existing path | `xyz` | Error `NotFound` |
236 ///
237 /// Refer to [RFC: List Prefix][crate::docs::rfcs::rfc_3243_list_prefix] for more details.
238 ///
239 /// ## Services that not support `create_dir`
240 ///
241 /// For services that not support `create_dir`, `stat("test/")` will return `NotFound` even
242 /// when `test/abc` exists since the service won't have the concept of dir. There is nothing
243 /// we can do about this.
244 ///
245 /// # Examples
246 ///
247 /// ## Check if file exists
248 ///
249 /// ```
250 /// # use anyhow::Result;
251 /// # use futures::io;
252 /// use opendal_core::blocking;
253 /// # use opendal_core::blocking::Operator;
254 /// use opendal_core::ErrorKind;
255 /// #
256 /// # fn test(op: blocking::Operator) -> Result<()> {
257 /// if let Err(e) = op.stat("test") {
258 /// if e.kind() == ErrorKind::NotFound {
259 /// println!("file not exist")
260 /// }
261 /// }
262 /// # Ok(())
263 /// # }
264 /// ```
265 pub fn stat(&self, path: &str) -> Result<Metadata> {
266 self.stat_options(path, options::StatOptions::default())
267 }
268
269 /// Get given path's metadata with extra options.
270 ///
271 /// # Behavior
272 ///
273 /// ## Services that support `create_dir`
274 ///
275 /// `test` and `test/` may vary in some services such as S3. However, on a local file system,
276 /// they're identical. Therefore, the behavior of `stat("test")` and `stat("test/")` might differ
277 /// in certain edge cases. Always use `stat("test/")` when you need to access a directory if possible.
278 ///
279 /// Here are the behavior list:
280 ///
281 /// | Case | Path | Result |
282 /// |------------------------|-----------------|--------------------------------------------|
283 /// | stat existing dir | `abc/` | Metadata with dir mode |
284 /// | stat existing file | `abc/def_file` | Metadata with file mode |
285 /// | stat dir without `/` | `abc/def_dir` | Error `NotFound` or metadata with dir mode |
286 /// | stat file with `/` | `abc/def_file/` | Error `NotFound` |
287 /// | stat not existing path | `xyz` | Error `NotFound` |
288 ///
289 /// Refer to [RFC: List Prefix][crate::docs::rfcs::rfc_3243_list_prefix] for more details.
290 ///
291 /// ## Services that not support `create_dir`
292 ///
293 /// For services that not support `create_dir`, `stat("test/")` will return `NotFound` even
294 /// when `test/abc` exists since the service won't have the concept of dir. There is nothing
295 /// we can do about this.
296 pub fn stat_options(&self, path: &str, opts: options::StatOptions) -> Result<Metadata> {
297 let op = self.op.clone();
298 let path = path.to_string();
299 self.spawn_block(async move { op.stat_options(&path, opts).await })?
300 }
301
302 /// Check if this path exists or not.
303 ///
304 /// # Example
305 ///
306 /// ```no_run
307 /// use anyhow::Result;
308 /// use opendal_core::blocking;
309 /// use opendal_core::blocking::Operator;
310 /// fn test(op: blocking::Operator) -> Result<()> {
311 /// let _ = op.exists("test")?;
312 ///
313 /// Ok(())
314 /// }
315 /// ```
316 pub fn exists(&self, path: &str) -> Result<bool> {
317 let r = self.stat(path);
318 match r {
319 Ok(_) => Ok(true),
320 Err(err) => match err.kind() {
321 ErrorKind::NotFound => Ok(false),
322 _ => Err(err),
323 },
324 }
325 }
326
327 /// Create a dir at given path.
328 ///
329 /// # Notes
330 ///
331 /// To indicate that a path is a directory, it is compulsory to include
332 /// a trailing / in the path. Failure to do so may result in
333 /// `NotADirectory` error being returned by OpenDAL.
334 ///
335 /// # Behavior
336 ///
337 /// - Create on existing dir will succeed.
338 /// - Create dir is always recursive, works like `mkdir -p`
339 ///
340 /// # Examples
341 ///
342 /// ```no_run
343 /// # use opendal_core::Result;
344 /// use opendal_core::blocking;
345 /// # use opendal_core::blocking::Operator;
346 /// # use futures::TryStreamExt;
347 /// # fn test(op: blocking::Operator) -> Result<()> {
348 /// op.create_dir("path/to/dir/")?;
349 /// # Ok(())
350 /// # }
351 /// ```
352 pub fn create_dir(&self, path: &str) -> Result<()> {
353 let op = self.op.clone();
354 let path = path.to_string();
355 self.spawn_block(async move { op.create_dir(&path).await })?
356 }
357
358 /// Read the whole path into a bytes.
359 ///
360 /// This function will allocate a new bytes internally. For more precise memory control or
361 /// reading data lazily, please use [`blocking::Operator::reader`]
362 ///
363 /// # Examples
364 ///
365 /// ```no_run
366 /// # use opendal_core::Result;
367 /// use opendal_core::blocking;
368 /// # use opendal_core::blocking::Operator;
369 /// #
370 /// # fn test(op: blocking::Operator) -> Result<()> {
371 /// let bs = op.read("path/to/file")?;
372 /// # Ok(())
373 /// # }
374 /// ```
375 pub fn read(&self, path: &str) -> Result<Buffer> {
376 self.read_options(path, options::ReadOptions::default())
377 }
378
379 /// Read the whole path into a bytes with extra options.
380 ///
381 /// This function will allocate a new bytes internally. For more precise memory control or
382 /// reading data lazily, please use [`blocking::Operator::reader`]
383 pub fn read_options(&self, path: &str, opts: options::ReadOptions) -> Result<Buffer> {
384 let op = self.op.clone();
385 let path = path.to_string();
386 self.spawn_block(async move { op.read_options(&path, opts).await })?
387 }
388
389 /// Create a new reader which can read the whole path.
390 ///
391 /// # Examples
392 ///
393 /// ```no_run
394 /// # use opendal_core::Result;
395 /// use opendal_core::blocking;
396 /// # use opendal_core::blocking::Operator;
397 /// # use futures::TryStreamExt;
398 /// # fn test(op: blocking::Operator) -> Result<()> {
399 /// let r = op.reader("path/to/file")?;
400 /// # Ok(())
401 /// # }
402 /// ```
403 pub fn reader(&self, path: &str) -> Result<blocking::Reader> {
404 self.reader_options(path, options::ReaderOptions::default())
405 }
406
407 /// Create a new reader with extra options
408 pub fn reader_options(
409 &self,
410 path: &str,
411 opts: options::ReaderOptions,
412 ) -> Result<blocking::Reader> {
413 let r = self.handle.block_on(self.op.reader_options(path, opts))?;
414 Ok(blocking::Reader::new(self.handle.clone(), r))
415 }
416
417 /// Write bytes into given path.
418 ///
419 /// # Notes
420 ///
421 /// - Write will make sure all bytes has been written, or an error will be returned.
422 ///
423 /// # Examples
424 ///
425 /// ```no_run
426 /// # use opendal_core::Result;
427 /// # use opendal_core::blocking::Operator;
428 /// # use futures::StreamExt;
429 /// # use futures::SinkExt;
430 /// use bytes::Bytes;
431 /// use opendal_core::blocking;
432 ///
433 /// # fn test(op: blocking::Operator) -> Result<()> {
434 /// op.write("path/to/file", vec![0; 4096])?;
435 /// # Ok(())
436 /// # }
437 /// ```
438 pub fn write(&self, path: &str, bs: impl Into<Buffer>) -> Result<Metadata> {
439 self.write_options(path, bs, options::WriteOptions::default())
440 }
441
442 /// Write data with options.
443 ///
444 /// # Notes
445 ///
446 /// - Write will make sure all bytes has been written, or an error will be returned.
447 pub fn write_options(
448 &self,
449 path: &str,
450 bs: impl Into<Buffer>,
451 opts: options::WriteOptions,
452 ) -> Result<Metadata> {
453 let op = self.op.clone();
454 let path = path.to_string();
455 let bs = bs.into();
456 self.spawn_block(async move { op.write_options(&path, bs, opts).await })?
457 }
458
459 /// Write multiple bytes into given path.
460 ///
461 /// # Notes
462 ///
463 /// - Write will make sure all bytes has been written, or an error will be returned.
464 ///
465 /// # Examples
466 ///
467 /// ```no_run
468 /// # use opendal_core::Result;
469 /// # use opendal_core::blocking;
470 /// # use opendal_core::blocking::Operator;
471 /// # use futures::StreamExt;
472 /// # use futures::SinkExt;
473 /// use bytes::Bytes;
474 ///
475 /// # fn test(op: blocking::Operator) -> Result<()> {
476 /// let mut w = op.writer("path/to/file")?;
477 /// w.write(vec![0; 4096])?;
478 /// w.write(vec![1; 4096])?;
479 /// w.close()?;
480 /// # Ok(())
481 /// # }
482 /// ```
483 pub fn writer(&self, path: &str) -> Result<blocking::Writer> {
484 self.writer_options(path, options::WriteOptions::default())
485 }
486
487 /// Create a new writer with extra options
488 pub fn writer_options(
489 &self,
490 path: &str,
491 opts: options::WriteOptions,
492 ) -> Result<blocking::Writer> {
493 let w = self.handle.block_on(self.op.writer_options(path, opts))?;
494 Ok(blocking::Writer::new(self.handle.clone(), w))
495 }
496
497 /// Copy a file from `from` to `to`.
498 ///
499 /// # Notes
500 ///
501 /// - `from` and `to` must be a file.
502 /// - `to` will be overwritten if it exists.
503 /// - If `from` and `to` are the same, nothing will happen.
504 /// - `copy` is idempotent. For same `from` and `to` input, the result will be the same.
505 ///
506 /// # Examples
507 ///
508 /// ```
509 /// # use opendal_core::Result;
510 /// use opendal_core::blocking;
511 /// # use opendal_core::blocking::Operator;
512 ///
513 /// # fn test(op: blocking::Operator) -> Result<()> {
514 /// op.copy("path/to/file", "path/to/file2")?;
515 /// # Ok(())
516 /// # }
517 /// ```
518 pub fn copy(&self, from: &str, to: &str) -> Result<Metadata> {
519 self.copy_options(from, to, options::CopyOptions::default())
520 }
521
522 /// Copy a file from `from` to `to` with additional options.
523 pub fn copy_options(
524 &self,
525 from: &str,
526 to: &str,
527 opts: options::CopyOptions,
528 ) -> Result<Metadata> {
529 let op = self.op.clone();
530 let from = from.to_string();
531 let to = to.to_string();
532 self.spawn_block(async move { op.copy_options(&from, &to, opts).await })?
533 }
534
535 /// Create a copier from `from` to `to`.
536 ///
537 /// This function creates a new [`blocking::Copier`] that implements
538 /// `Iterator<Item = Result<usize>>`.
539 pub fn copier(&self, from: &str, to: &str) -> Result<blocking::Copier> {
540 self.copier_options(from, to, options::CopyOptions::default())
541 }
542
543 /// Create a copier from `from` to `to` with additional options.
544 pub fn copier_options(
545 &self,
546 from: &str,
547 to: &str,
548 opts: options::CopyOptions,
549 ) -> Result<blocking::Copier> {
550 let copier = self
551 .handle
552 .block_on(self.op.copier_options(from, to, opts))?;
553 Ok(blocking::Copier::new(self.handle.clone(), copier))
554 }
555
556 /// Rename a file from `from` to `to`.
557 ///
558 /// # Notes
559 ///
560 /// - `from` and `to` must be a file.
561 /// - `to` will be overwritten if it exists.
562 /// - If `from` and `to` are the same, a `IsSameFile` error will occur.
563 ///
564 /// # Examples
565 ///
566 /// ```
567 /// # use opendal_core::Result;
568 /// use opendal_core::blocking;
569 /// # use opendal_core::blocking::Operator;
570 ///
571 /// # fn test(op: blocking::Operator) -> Result<()> {
572 /// op.rename("path/to/file", "path/to/file2")?;
573 /// # Ok(())
574 /// # }
575 /// ```
576 pub fn rename(&self, from: &str, to: &str) -> Result<()> {
577 let op = self.op.clone();
578 let from = from.to_string();
579 let to = to.to_string();
580 self.spawn_block(async move { op.rename(&from, &to).await })?
581 }
582
583 /// Delete given path.
584 ///
585 /// # Notes
586 ///
587 /// - Delete not existing error won't return errors.
588 ///
589 /// # Examples
590 ///
591 /// ```no_run
592 /// # use anyhow::Result;
593 /// # use futures::io;
594 /// use opendal_core::blocking;
595 /// # use opendal_core::blocking::Operator;
596 /// # fn test(op: blocking::Operator) -> Result<()> {
597 /// op.delete("path/to/file")?;
598 /// # Ok(())
599 /// # }
600 /// ```
601 pub fn delete(&self, path: &str) -> Result<()> {
602 self.delete_options(path, options::DeleteOptions::default())
603 }
604
605 /// Delete given path with options.
606 ///
607 /// # Notes
608 ///
609 /// - Delete not existing error won't return errors.
610 pub fn delete_options(&self, path: &str, opts: options::DeleteOptions) -> Result<()> {
611 let op = self.op.clone();
612 let path = path.to_string();
613 self.spawn_block(async move { op.delete_options(&path, opts).await })?
614 }
615
616 /// Delete an infallible iterator of paths.
617 ///
618 /// Also see:
619 ///
620 /// - [`blocking::Operator::delete_try_iter`]: delete an fallible iterator of paths.
621 pub fn delete_iter<I, D>(&self, iter: I) -> Result<()>
622 where
623 I: IntoIterator<Item = D>,
624 D: IntoDeleteInput,
625 {
626 self.handle.block_on(self.op.delete_iter(iter))
627 }
628
629 /// Delete a fallible iterator of paths.
630 ///
631 /// Also see:
632 ///
633 /// - [`blocking::Operator::delete_iter`]: delete an infallible iterator of paths.
634 pub fn delete_try_iter<I, D>(&self, try_iter: I) -> Result<()>
635 where
636 I: IntoIterator<Item = Result<D>>,
637 D: IntoDeleteInput,
638 {
639 self.handle.block_on(self.op.delete_try_iter(try_iter))
640 }
641
642 /// Create a [`BlockingDeleter`] to continuously remove content from storage.
643 ///
644 /// It leverages batch deletion capabilities provided by storage services for efficient removal.
645 ///
646 /// Users can have more control over the deletion process by using [`BlockingDeleter`] directly.
647 pub fn deleter(&self) -> Result<blocking::Deleter> {
648 blocking::Deleter::create(
649 self.handle.clone(),
650 self.handle.block_on(self.op.deleter())?,
651 )
652 }
653
654 /// Remove the path and all nested dirs and files recursively.
655 ///
656 /// # Deprecated
657 ///
658 /// This method is deprecated since v0.55.0. Use [`blocking::Operator::delete_options`] with
659 /// `recursive: true` instead.
660 ///
661 /// ## Migration Example
662 ///
663 /// Instead of:
664 /// ```ignore
665 /// op.remove_all("path/to/dir")?;
666 /// ```
667 ///
668 /// Use:
669 /// ```ignore
670 /// use opendal_core::options::DeleteOptions;
671 /// op.delete_options("path/to/dir", DeleteOptions {
672 /// recursive: true,
673 /// ..Default::default()
674 /// })?;
675 /// ```
676 ///
677 /// # Notes
678 ///
679 /// If underlying services support delete in batch, we will use batch
680 /// delete instead.
681 ///
682 /// # Examples
683 ///
684 /// ```
685 /// # use anyhow::Result;
686 /// # use futures::io;
687 /// use opendal_core::blocking;
688 /// # use opendal_core::blocking::Operator;
689 /// # fn test(op: blocking::Operator) -> Result<()> {
690 /// op.remove_all("path/to/dir")?;
691 /// # Ok(())
692 /// # }
693 /// ```
694 #[deprecated(
695 since = "0.55.0",
696 note = "Use `delete_options` with `recursive: true` instead"
697 )]
698 #[allow(deprecated)]
699 pub fn remove_all(&self, path: &str) -> Result<()> {
700 self.delete_options(
701 path,
702 options::DeleteOptions {
703 recursive: true,
704 ..Default::default()
705 },
706 )
707 }
708
709 /// List entries whose paths start with the given prefix `path`.
710 ///
711 /// # Semantics
712 ///
713 /// - Listing is **prefix-based**; it doesn't require the parent directory to exist.
714 /// - If `path` itself exists, it is returned as an entry along with prefixed children.
715 /// - If `path` is missing but deeper objects exist, the list succeeds and returns those prefixed entries instead of an error.
716 /// - 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.
717 ///
718 /// ## Streaming List
719 ///
720 /// This function materializes the full result in memory. For large listings, prefer [`blocking::Operator::lister`] to stream entries.
721 ///
722 /// # Examples
723 ///
724 /// ```no_run
725 /// # use anyhow::Result;
726 /// use opendal_core::blocking;
727 /// use opendal_core::blocking::Operator;
728 /// use opendal_core::EntryMode;
729 /// # fn test(op: blocking::Operator) -> Result<()> {
730 /// let mut entries = op.list("path/to/dir/")?;
731 /// for entry in entries {
732 /// match entry.metadata().mode() {
733 /// EntryMode::FILE => {
734 /// println!("Handling file")
735 /// }
736 /// EntryMode::DIR => {
737 /// println!("Handling dir {}", entry.path())
738 /// }
739 /// EntryMode::Unknown => continue,
740 /// }
741 /// }
742 /// # Ok(())
743 /// # }
744 /// ```
745 pub fn list(&self, path: &str) -> Result<Vec<Entry>> {
746 self.list_options(path, options::ListOptions::default())
747 }
748
749 /// List entries whose paths start with the given prefix `path` with additional options.
750 ///
751 /// # Semantics
752 ///
753 /// Inherits the prefix semantics described in [`Operator::list`] (blocking variant). It returns `path` itself if present and tolerates missing parents when prefixed objects exist.
754 ///
755 /// ## Streaming List
756 ///
757 /// This function materializes the full result in memory. For large listings, prefer [`blocking::Operator::lister`] to stream entries.
758 ///
759 /// ## Options
760 ///
761 /// See [`options::ListOptions`] for the full set. Common knobs: traversal (`recursive`), pagination (`limit`, `start_after`), and versioning (`versions`, `deleted`).
762 pub fn list_options(&self, path: &str, opts: options::ListOptions) -> Result<Vec<Entry>> {
763 let op = self.op.clone();
764 let path = path.to_string();
765 self.spawn_block(async move { op.list_options(&path, opts).await })?
766 }
767
768 /// Create a streaming lister for entries whose paths start with the given prefix `path`.
769 ///
770 /// This function creates a new [`BlockingLister`]; dropping it stops listing.
771 ///
772 /// # Semantics
773 ///
774 /// 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.
775 ///
776 /// ## Options
777 ///
778 /// Accepts the same [`options::ListOptions`] as [`list_options`](Self::list_options): traversal (`recursive`), pagination (`limit`, `start_after`), and versioning (`versions`, `deleted`).
779 ///
780 /// # Examples
781 ///
782 /// ```no_run
783 /// # use anyhow::Result;
784 /// # use futures::io;
785 /// use futures::TryStreamExt;
786 /// use opendal_core::blocking;
787 /// use opendal_core::blocking::Operator;
788 /// use opendal_core::EntryMode;
789 /// # fn test(op: blocking::Operator) -> Result<()> {
790 /// let mut ds = op.lister("path/to/dir/")?;
791 /// for de in ds {
792 /// let de = de?;
793 /// match de.metadata().mode() {
794 /// EntryMode::FILE => {
795 /// println!("Handling file")
796 /// }
797 /// EntryMode::DIR => {
798 /// println!("Handling dir like start a new list via meta.path()")
799 /// }
800 /// EntryMode::Unknown => continue,
801 /// }
802 /// }
803 /// # Ok(())
804 /// # }
805 /// ```
806 pub fn lister(&self, path: &str) -> Result<blocking::Lister> {
807 self.lister_options(path, options::ListOptions::default())
808 }
809
810 /// List entries under a prefix as an iterator with options.
811 ///
812 /// This function creates a new handle to stream entries and inherits the prefix semantics of [`blocking::Operator::list`].
813 ///
814 /// ## Options
815 ///
816 /// Same as [`lister`](Self::lister); see [`options::ListOptions`] for traversal, pagination, and versioning knobs.
817 pub fn lister_options(
818 &self,
819 path: &str,
820 opts: options::ListOptions,
821 ) -> Result<blocking::Lister> {
822 let l = self.handle.block_on(self.op.lister_options(path, opts))?;
823 Ok(blocking::Lister::new(self.handle.clone(), l))
824 }
825
826 /// Check if this operator can work correctly.
827 ///
828 /// We will send a `list` request to path and return any errors we met.
829 ///
830 /// ```
831 /// # use std::sync::Arc;
832 /// # use anyhow::Result;
833 /// use opendal_core::blocking;
834 /// use opendal_core::blocking::Operator;
835 /// use opendal_core::ErrorKind;
836 ///
837 /// # fn test(op: blocking::Operator) -> Result<()> {
838 /// op.check()?;
839 /// # Ok(())
840 /// # }
841 /// ```
842 pub fn check(&self) -> Result<()> {
843 let mut ds = self.lister("/")?;
844
845 match ds.next() {
846 Some(Err(e)) if e.kind() != ErrorKind::NotFound => Err(e),
847 _ => Ok(()),
848 }
849 }
850}
851
852impl From<Operator> for AsyncOperator {
853 fn from(val: Operator) -> Self {
854 val.op
855 }
856}