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
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    // rustdoc-stripper-ignore-next
28    /// Converts the enumerator into a [`Stream`](futures_core::Stream).
29    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
42// rustdoc-stripper-ignore-next
43/// A [`Stream`](futures_core::Stream) used to enumerate files in directories.
44pub 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        // propagate any error from the future into a panic
137        Rc::try_unwrap(res)
138            .unwrap_or_else(|_| panic!("future not finished"))
139            .into_inner()
140            .unwrap()
141            .unwrap();
142    }
143}