gio/
file_enumerator.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use 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    // rustdoc-stripper-ignore-next
23    /// Converts the enumerator into a [`Stream`](futures_core::Stream).
24    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
37// rustdoc-stripper-ignore-next
38/// A [`Stream`](futures_core::Stream) used to enumerate files in directories.
39pub 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        // propagate any error from the future into a panic
132        Rc::try_unwrap(res)
133            .unwrap_or_else(|_| panic!("future not finished"))
134            .into_inner()
135            .unwrap()
136            .unwrap();
137    }
138}