Skip to main content

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}