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