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