forked from async-rs/async-std
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathread_dir.rs
112 lines (104 loc) · 3.5 KB
/
read_dir.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use std::future::Future;
use std::pin::Pin;
use crate::fs::DirEntry;
use crate::io;
use crate::path::Path;
use crate::stream::Stream;
use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
use crate::utils::Context as _;
/// Returns a stream of entries in a directory.
///
/// The stream yields items of type [`io::Result`]`<`[`DirEntry`]`>`. Note that I/O errors can
/// occur while reading from the stream.
///
/// This function is an async version of [`std::fs::read_dir`].
///
/// [`io::Result`]: ../io/type.Result.html
/// [`DirEntry`]: struct.DirEntry.html
/// [`std::fs::read_dir`]: https://doc.rust-lang.org/std/fs/fn.read_dir.html
///
/// # Errors
///
/// An error will be returned in the following situations:
///
/// * `path` does not point to an existing directory.
/// * The current process lacks permissions to read the contents of the directory.
/// * Some other I/O error occurred.
///
/// # Examples
///
/// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::fs;
/// use async_std::prelude::*;
///
/// let mut entries = fs::read_dir(".").await?;
///
/// while let Some(res) = entries.next().await {
/// let entry = res?;
/// println!("{}", entry.file_name().to_string_lossy());
/// }
/// #
/// # Ok(()) }) }
/// ```
pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
let path = path.as_ref().to_owned();
spawn_blocking(move || {
std::fs::read_dir(&path)
.context(|| format!("could not read directory `{}`", path.display()))
})
.await
.map(ReadDir::new)
}
/// A stream of entries in a directory.
///
/// This stream is returned by [`read_dir`] and yields items of type
/// [`io::Result`]`<`[`DirEntry`]`>`. Each [`DirEntry`] can then retrieve information like entry's
/// path or metadata.
///
/// This type is an async version of [`std::fs::ReadDir`].
///
/// [`read_dir`]: fn.read_dir.html
/// [`io::Result`]: ../io/type.Result.html
/// [`DirEntry`]: struct.DirEntry.html
/// [`std::fs::ReadDir`]: https://doc.rust-lang.org/std/fs/struct.ReadDir.html
#[derive(Debug)]
pub struct ReadDir(State);
/// The state of an asynchronous `ReadDir`.
///
/// The `ReadDir` can be either idle or busy performing an asynchronous operation.
#[derive(Debug)]
enum State {
Idle(Option<std::fs::ReadDir>),
Busy(JoinHandle<(std::fs::ReadDir, Option<io::Result<std::fs::DirEntry>>)>),
}
impl ReadDir {
/// Creates an asynchronous `ReadDir` from a synchronous handle.
pub(crate) fn new(inner: std::fs::ReadDir) -> ReadDir {
ReadDir(State::Idle(Some(inner)))
}
}
impl Stream for ReadDir {
type Item = io::Result<DirEntry>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
loop {
match &mut self.0 {
State::Idle(opt) => {
let mut inner = opt.take().unwrap();
// Start the operation asynchronously.
self.0 = State::Busy(spawn_blocking(move || {
let next = inner.next();
(inner, next)
}));
}
// Poll the asynchronous operation the file is currently blocked on.
State::Busy(task) => {
let (inner, opt) = futures_core::ready!(Pin::new(task).poll(cx));
self.0 = State::Idle(Some(inner));
return Poll::Ready(opt.map(|res| res.map(DirEntry::new)));
}
}
}
}
}