Skip to content

Commit 292f9a3

Browse files
authored
Merge pull request #1035 from jf2048/file-enumerator-stream
gio: add FileEnumerator::into_stream
2 parents 2b91ec4 + 27a015e commit 292f9a3

File tree

4 files changed

+128
-3
lines changed

4 files changed

+128
-3
lines changed

gio/Gir.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,7 @@ manual_traits = ["FileDescriptorBasedExtManual"]
792792
[[object]]
793793
name = "Gio.FileEnumerator"
794794
status = "generate"
795+
manual_traits = ["FileEnumeratorExtManual"]
795796
[[object.function]]
796797
name = "iterate"
797798
# better with rust iterators

gio/src/file_enumerator.rs

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// Take a look at the license at the top of the repository in the LICENSE file.
22

3-
use std::iter::FusedIterator;
4-
53
use crate::{prelude::*, FileEnumerator, FileInfo};
4+
use futures_core::future::LocalBoxFuture;
5+
use futures_util::FutureExt;
6+
use std::{iter::FusedIterator, task::Poll};
67

78
impl Iterator for FileEnumerator {
89
type Item = Result<FileInfo, glib::Error>;
@@ -16,3 +17,124 @@ impl Iterator for FileEnumerator {
1617
}
1718

1819
impl FusedIterator for FileEnumerator {}
20+
21+
pub trait FileEnumeratorExtManual {
22+
fn into_stream(self, num_files: i32, priority: glib::Priority) -> FileEnumeratorStream;
23+
}
24+
25+
impl<O: IsA<FileEnumerator>> FileEnumeratorExtManual for O {
26+
// rustdoc-stripper-ignore-next
27+
/// Converts the enumerator into a [`Stream`](futures_core::Stream).
28+
fn into_stream(self, num_files: i32, priority: glib::Priority) -> FileEnumeratorStream {
29+
let future = Some(self.next_files_future(num_files, priority));
30+
FileEnumeratorStream {
31+
enumerator: self.upcast(),
32+
future,
33+
num_files,
34+
priority,
35+
}
36+
}
37+
}
38+
39+
// rustdoc-stripper-ignore-next
40+
/// A [`Stream`](futures_core::Stream) used to enumerate files in directories.
41+
pub struct FileEnumeratorStream {
42+
enumerator: FileEnumerator,
43+
future: Option<LocalBoxFuture<'static, Result<Vec<FileInfo>, glib::Error>>>,
44+
num_files: i32,
45+
priority: glib::Priority,
46+
}
47+
48+
impl std::fmt::Debug for FileEnumeratorStream {
49+
#[inline]
50+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51+
f.debug_struct("FileEnumeratorStream")
52+
.field("enumerator", &self.enumerator)
53+
.field("num_files", &self.num_files)
54+
.field("priority", &self.priority)
55+
.finish()
56+
}
57+
}
58+
59+
impl futures_core::Stream for FileEnumeratorStream {
60+
type Item = Result<Vec<FileInfo>, glib::Error>;
61+
62+
#[inline]
63+
fn poll_next(
64+
mut self: std::pin::Pin<&mut Self>,
65+
cx: &mut std::task::Context<'_>,
66+
) -> Poll<Option<Self::Item>> {
67+
match self.future.take() {
68+
Some(mut f) => match f.poll_unpin(cx) {
69+
Poll::Ready(Ok(fs)) if fs.is_empty() => Poll::Ready(None),
70+
Poll::Ready(Ok(fs)) => {
71+
self.future = Some(
72+
self.enumerator
73+
.next_files_future(self.num_files, self.priority),
74+
);
75+
Poll::Ready(Some(Ok(fs)))
76+
}
77+
Poll::Ready(Err(e)) => Poll::Ready(Some(Err(e))),
78+
Poll::Pending => {
79+
self.future = Some(f);
80+
Poll::Pending
81+
}
82+
},
83+
None => Poll::Ready(None),
84+
}
85+
}
86+
}
87+
88+
impl futures_core::FusedStream for FileEnumeratorStream {
89+
#[inline]
90+
fn is_terminated(&self) -> bool {
91+
self.future.is_none()
92+
}
93+
}
94+
95+
#[cfg(test)]
96+
mod tests {
97+
use crate::prelude::*;
98+
use futures_util::StreamExt;
99+
use std::{cell::Cell, rc::Rc};
100+
#[test]
101+
fn file_enumerator_stream() {
102+
let dir = std::env::current_dir().unwrap();
103+
let ctx = glib::MainContext::new();
104+
let lp = glib::MainLoop::new(Some(&ctx), false);
105+
let res = Rc::new(Cell::new(None));
106+
107+
let lp_clone = lp.clone();
108+
let res_clone = res.clone();
109+
ctx.spawn_local(async move {
110+
res_clone.replace(Some(
111+
async {
112+
let dir = crate::File::for_path(dir);
113+
let mut stream = dir
114+
.enumerate_children_future(
115+
crate::FILE_ATTRIBUTE_STANDARD_NAME,
116+
crate::FileQueryInfoFlags::NONE,
117+
glib::Priority::default(),
118+
)
119+
.await?
120+
.into_stream(4, glib::Priority::default());
121+
while let Some(files) = stream.next().await {
122+
for file in files? {
123+
let _ = file.name();
124+
}
125+
}
126+
Ok::<_, glib::Error>(())
127+
}
128+
.await,
129+
));
130+
lp_clone.quit();
131+
});
132+
lp.run();
133+
// propagate any error from the future into a panic
134+
Rc::try_unwrap(res)
135+
.unwrap_or_else(|_| panic!("future not finished"))
136+
.into_inner()
137+
.unwrap()
138+
.unwrap();
139+
}
140+
}

gio/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ mod file_descriptor_based;
4848
#[cfg(any(unix, feature = "dox"))]
4949
pub use file_descriptor_based::FileDescriptorBased;
5050
mod file_enumerator;
51+
pub use crate::file_enumerator::FileEnumeratorStream;
5152
mod file_info;
5253
mod flags;
5354
mod inet_address;

gio/src/prelude.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ pub use crate::unix_socket_address::{UnixSocketAddressExtManual, UnixSocketAddre
2929
pub use crate::{
3030
action_map::ActionMapExtManual, application::*, auto::traits::*, cancellable::*, converter::*,
3131
data_input_stream::DataInputStreamExtManual, datagram_based::*, dbus_proxy::DBusProxyExtManual,
32-
file::FileExtManual, inet_address::InetAddressExtManual, input_stream::InputStreamExtManual,
32+
file::FileExtManual, file_enumerator::FileEnumeratorExtManual,
33+
inet_address::InetAddressExtManual, input_stream::InputStreamExtManual,
3334
io_stream::IOStreamExtManual, list_model::ListModelExtManual,
3435
output_stream::OutputStreamExtManual, pollable_input_stream::PollableInputStreamExtManual,
3536
pollable_output_stream::PollableOutputStreamExtManual, settings::SettingsExtManual,

0 commit comments

Comments
 (0)