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