sqlx_core/
fs.rs

1use std::ffi::OsString;
2use std::fs::Metadata;
3use std::io;
4use std::path::{Path, PathBuf};
5
6use crate::rt;
7
8pub struct ReadDir {
9    inner: Option<std::fs::ReadDir>,
10}
11
12pub struct DirEntry {
13    pub path: PathBuf,
14    pub file_name: OsString,
15    pub metadata: Metadata,
16}
17
18// Filesystem operations are generally not capable of being non-blocking
19// so Tokio and async-std don't bother; they just send the work to a blocking thread pool.
20//
21// We save on code duplication here by just implementing the same strategy ourselves
22// using the runtime's `spawn_blocking()` primitive.
23
24pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
25    let path = PathBuf::from(path.as_ref());
26    rt::spawn_blocking(move || std::fs::read(path)).await
27}
28
29pub async fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
30    let path = PathBuf::from(path.as_ref());
31    rt::spawn_blocking(move || std::fs::read_to_string(path)).await
32}
33
34pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
35    let path = PathBuf::from(path.as_ref());
36    rt::spawn_blocking(move || std::fs::create_dir_all(path)).await
37}
38
39pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
40    let path = PathBuf::from(path.as_ref());
41    rt::spawn_blocking(move || std::fs::remove_file(path)).await
42}
43
44pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
45    let path = PathBuf::from(path.as_ref());
46    rt::spawn_blocking(move || std::fs::remove_dir(path)).await
47}
48
49pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
50    let path = PathBuf::from(path.as_ref());
51    rt::spawn_blocking(move || std::fs::remove_dir_all(path)).await
52}
53
54pub async fn read_dir(path: PathBuf) -> io::Result<ReadDir> {
55    let read_dir = rt::spawn_blocking(move || std::fs::read_dir(path)).await?;
56
57    Ok(ReadDir {
58        inner: Some(read_dir),
59    })
60}
61
62impl ReadDir {
63    pub async fn next(&mut self) -> io::Result<Option<DirEntry>> {
64        if let Some(mut read_dir) = self.inner.take() {
65            let maybe = rt::spawn_blocking(move || {
66                let entry = read_dir.next().transpose()?;
67
68                entry
69                    .map(|entry| -> io::Result<_> {
70                        Ok((
71                            read_dir,
72                            DirEntry {
73                                path: entry.path(),
74                                file_name: entry.file_name(),
75                                // We always want the metadata as well so might as well fetch
76                                // it in the same blocking call.
77                                metadata: entry.metadata()?,
78                            },
79                        ))
80                    })
81                    .transpose()
82            })
83            .await?;
84
85            match maybe {
86                Some((read_dir, entry)) => {
87                    self.inner = Some(read_dir);
88                    Ok(Some(entry))
89                }
90                None => Ok(None),
91            }
92        } else {
93            Ok(None)
94        }
95    }
96}