Skip to main content

gio/
file_enumerator.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use 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) => 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    // rustdoc-stripper-ignore-next
24    /// Converts the enumerator into a [`Stream`](futures_core::Stream).
25    fn into_stream(self, num_files: i32, priority: glib::Priority) -> FileEnumeratorStream {
26        let future: Option<std::pin::Pin<Box<dyn Future<Output = _>>>> =
27            Some(Box::pin(self.next_files_future(num_files, priority)));
28        FileEnumeratorStream {
29            enumerator: self.upcast(),
30            future,
31            num_files,
32            priority,
33        }
34    }
35
36    /// Releases all resources used by this enumerator.
37    ///
38    /// The enumerator will return [error@Gio.IOErrorEnum.CLOSED] on all subsequent
39    /// calls.
40    ///
41    /// This will be automatically called when the last reference
42    /// is dropped, but you might want to call this function to make
43    /// sure resources are released as early as possible.
44    /// ## `cancellable`
45    /// optional cancellable object
46    ///
47    /// # Returns
48    ///
49    /// true on success; false otherwise
50    #[doc(alias = "g_file_enumerator_close")]
51    fn close(
52        &self,
53        cancellable: Option<&impl IsA<crate::Cancellable>>,
54    ) -> (bool, Option<glib::Error>) {
55        unsafe {
56            let mut error = std::ptr::null_mut();
57            let ret = crate::ffi::g_file_enumerator_close(
58                self.as_ref().to_glib_none().0,
59                cancellable.map(|p| p.as_ref()).to_glib_none().0,
60                &mut error,
61            );
62            (from_glib(ret), from_glib_full(error))
63        }
64    }
65}
66
67impl<O: IsA<FileEnumerator>> FileEnumeratorExtManual for O {}
68
69// rustdoc-stripper-ignore-next
70/// A [`Stream`](futures_core::Stream) used to enumerate files in directories.
71pub struct FileEnumeratorStream {
72    enumerator: FileEnumerator,
73    future: Option<LocalBoxFuture<'static, Result<Vec<FileInfo>, glib::Error>>>,
74    num_files: i32,
75    priority: glib::Priority,
76}
77
78impl std::fmt::Debug for FileEnumeratorStream {
79    #[inline]
80    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81        f.debug_struct("FileEnumeratorStream")
82            .field("enumerator", &self.enumerator)
83            .field("num_files", &self.num_files)
84            .field("priority", &self.priority)
85            .finish()
86    }
87}
88
89impl futures_core::Stream for FileEnumeratorStream {
90    type Item = Result<Vec<FileInfo>, glib::Error>;
91
92    #[inline]
93    fn poll_next(
94        mut self: std::pin::Pin<&mut Self>,
95        cx: &mut std::task::Context<'_>,
96    ) -> Poll<Option<Self::Item>> {
97        match self.future.take() {
98            Some(mut f) => match f.poll_unpin(cx) {
99                Poll::Ready(Ok(fs)) if fs.is_empty() => Poll::Ready(None),
100                Poll::Ready(Ok(fs)) => {
101                    self.future = Some(Box::pin(
102                        self.enumerator
103                            .next_files_future(self.num_files, self.priority),
104                    ));
105                    Poll::Ready(Some(Ok(fs)))
106                }
107                Poll::Ready(Err(e)) => Poll::Ready(Some(Err(e))),
108                Poll::Pending => {
109                    self.future = Some(f);
110                    Poll::Pending
111                }
112            },
113            None => Poll::Ready(None),
114        }
115    }
116}
117
118impl futures_core::FusedStream for FileEnumeratorStream {
119    #[inline]
120    fn is_terminated(&self) -> bool {
121        self.future.is_none()
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use crate::prelude::*;
128    use futures_util::StreamExt;
129    use std::{cell::Cell, rc::Rc};
130    #[test]
131    fn file_enumerator_stream() {
132        let dir = std::env::current_dir().unwrap();
133        let ctx = glib::MainContext::new();
134        let lp = glib::MainLoop::new(Some(&ctx), false);
135        let res = Rc::new(Cell::new(None));
136
137        let lp_clone = lp.clone();
138        let res_clone = res.clone();
139        ctx.spawn_local(async move {
140            res_clone.replace(Some(
141                async {
142                    let dir = crate::File::for_path(dir);
143                    let mut stream = dir
144                        .enumerate_children_future(
145                            crate::FILE_ATTRIBUTE_STANDARD_NAME,
146                            crate::FileQueryInfoFlags::NONE,
147                            glib::Priority::default(),
148                        )
149                        .await?
150                        .into_stream(4, glib::Priority::default());
151                    while let Some(files) = stream.next().await {
152                        for file in files? {
153                            let _ = file.name();
154                        }
155                    }
156                    Ok::<_, glib::Error>(())
157                }
158                .await,
159            ));
160            lp_clone.quit();
161        });
162        lp.run();
163        // propagate any error from the future into a panic
164        Rc::try_unwrap(res)
165            .unwrap_or_else(|_| panic!("future not finished"))
166            .into_inner()
167            .unwrap()
168            .unwrap();
169    }
170}