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