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