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) if err.kind::<crate::IOErrorEnum>() == Some(crate::IOErrorEnum::Closed) => {
17 None
18 }
19 Err(err) => Some(Err(err)),
20 Ok(file_info) => file_info.map(Ok),
21 }
22 }
23}
24
25impl FusedIterator for FileEnumerator {}
26
27pub trait FileEnumeratorExtManual: IsA<FileEnumerator> {
28 fn into_stream(self, num_files: i32, priority: glib::Priority) -> FileEnumeratorStream {
31 let future: Option<std::pin::Pin<Box<dyn Future<Output = _>>>> =
32 Some(Box::pin(self.next_files_future(num_files, priority)));
33 FileEnumeratorStream {
34 enumerator: self.upcast(),
35 future,
36 num_files,
37 priority,
38 }
39 }
40
41 #[doc(alias = "g_file_enumerator_close")]
56 fn close(
57 &self,
58 cancellable: Option<&impl IsA<crate::Cancellable>>,
59 ) -> (bool, Option<glib::Error>) {
60 unsafe {
61 let mut error = std::ptr::null_mut();
62 let ret = crate::ffi::g_file_enumerator_close(
63 self.as_ref().to_glib_none().0,
64 cancellable.map(|p| p.as_ref()).to_glib_none().0,
65 &mut error,
66 );
67 (from_glib(ret), from_glib_full(error))
68 }
69 }
70}
71
72impl<O: IsA<FileEnumerator>> FileEnumeratorExtManual for O {}
73
74pub struct FileEnumeratorStream {
77 enumerator: FileEnumerator,
78 future: Option<LocalBoxFuture<'static, Result<Vec<FileInfo>, glib::Error>>>,
79 num_files: i32,
80 priority: glib::Priority,
81}
82
83impl std::fmt::Debug for FileEnumeratorStream {
84 #[inline]
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 f.debug_struct("FileEnumeratorStream")
87 .field("enumerator", &self.enumerator)
88 .field("num_files", &self.num_files)
89 .field("priority", &self.priority)
90 .finish()
91 }
92}
93
94impl futures_core::Stream for FileEnumeratorStream {
95 type Item = Result<Vec<FileInfo>, glib::Error>;
96
97 #[inline]
98 fn poll_next(
99 mut self: std::pin::Pin<&mut Self>,
100 cx: &mut std::task::Context<'_>,
101 ) -> Poll<Option<Self::Item>> {
102 match self.future.take() {
103 Some(mut f) => match f.poll_unpin(cx) {
104 Poll::Ready(Ok(fs)) if fs.is_empty() => Poll::Ready(None),
105 Poll::Ready(Ok(fs)) => {
106 self.future = Some(Box::pin(
107 self.enumerator
108 .next_files_future(self.num_files, self.priority),
109 ));
110 Poll::Ready(Some(Ok(fs)))
111 }
112 Poll::Ready(Err(e)) => Poll::Ready(Some(Err(e))),
113 Poll::Pending => {
114 self.future = Some(f);
115 Poll::Pending
116 }
117 },
118 None => Poll::Ready(None),
119 }
120 }
121}
122
123impl futures_core::FusedStream for FileEnumeratorStream {
124 #[inline]
125 fn is_terminated(&self) -> bool {
126 self.future.is_none()
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use crate::prelude::*;
133 use futures_util::StreamExt;
134 use std::{cell::Cell, rc::Rc};
135 #[test]
136 fn file_enumerator_stream() {
137 let dir = std::env::current_dir().unwrap();
138 let ctx = glib::MainContext::new();
139 let lp = glib::MainLoop::new(Some(&ctx), false);
140 let res = Rc::new(Cell::new(None));
141
142 let lp_clone = lp.clone();
143 let res_clone = res.clone();
144 ctx.spawn_local(async move {
145 res_clone.replace(Some(
146 async {
147 let dir = crate::File::for_path(dir);
148 let mut stream = dir
149 .enumerate_children_future(
150 crate::FILE_ATTRIBUTE_STANDARD_NAME,
151 crate::FileQueryInfoFlags::NONE,
152 glib::Priority::default(),
153 )
154 .await?
155 .into_stream(4, glib::Priority::default());
156 while let Some(files) = stream.next().await {
157 for file in files? {
158 let _ = file.name();
159 }
160 }
161 Ok::<_, glib::Error>(())
162 }
163 .await,
164 ));
165 lp_clone.quit();
166 });
167 lp.run();
168 Rc::try_unwrap(res)
170 .unwrap_or_else(|_| panic!("future not finished"))
171 .into_inner()
172 .unwrap()
173 .unwrap();
174 }
175
176 #[test]
177 fn file_enumerator_iterator_closed() {
178 let dir = crate::File::for_path(std::env::current_dir().unwrap());
179 let mut enumerator = dir
180 .enumerate_children(
181 crate::FILE_ATTRIBUTE_STANDARD_NAME,
182 crate::FileQueryInfoFlags::NONE,
183 crate::Cancellable::NONE,
184 )
185 .unwrap();
186
187 while enumerator.next().is_some() {}
189
190 assert!(enumerator.close(crate::Cancellable::NONE).0);
193 assert!(enumerator.next().is_none());
194 assert!(enumerator.next().is_none());
195 }
196}