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