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