1use glib::{prelude::*, subclass::prelude::*, translate::*};
4use std::pin::Pin;
5
6use crate::{
7 AsyncResult, Cancellable, FileEnumerator, FileInfo, GioFuture, IOErrorEnum, LocalTask, ffi,
8 prelude::*,
9};
10
11pub trait FileEnumeratorImpl: ObjectImpl + ObjectSubclass<Type: IsA<FileEnumerator>> {
14 fn next_file(
34 &self,
35 cancellable: Option<&Cancellable>,
36 ) -> Result<Option<FileInfo>, glib::Error> {
37 self.parent_next_file(cancellable)
38 }
39
40 fn next_files_future(
41 &self,
42 num_files: i32,
43 priority: glib::Priority,
44 ) -> std::pin::Pin<
45 Box<dyn std::future::Future<Output = Result<glib::List<FileInfo>, glib::Error>> + 'static>,
46 > {
47 self.parent_next_files_future(num_files, priority)
48 }
49
50 fn close(&self, cancellable: Option<&Cancellable>) -> (bool, Option<glib::Error>) {
81 self.parent_close(cancellable)
82 }
83
84 fn close_future(
85 &self,
86 io_priority: glib::Priority,
87 ) -> Pin<Box<dyn Future<Output = Result<(), glib::Error>> + 'static>> {
88 self.parent_close_future(io_priority)
89 }
90}
91
92pub trait FileEnumeratorImplExt: FileEnumeratorImpl {
95 fn parent_next_file(
96 &self,
97 cancellable: Option<&Cancellable>,
98 ) -> Result<Option<FileInfo>, glib::Error> {
99 if self.obj().is_closed() {
100 Err(glib::Error::new::<IOErrorEnum>(
101 IOErrorEnum::Closed,
102 "Enumerator is closed",
103 ))
104 } else {
105 unsafe {
106 let data = Self::type_data();
107 let parent_class = data.as_ref().parent_class() as *const ffi::GFileEnumeratorClass;
108
109 let f = (*parent_class)
110 .next_file
111 .expect("No parent class implementation for \"next_file\"");
112
113 let mut error = std::ptr::null_mut();
114 let res = f(
115 self.obj()
116 .unsafe_cast_ref::<FileEnumerator>()
117 .to_glib_none()
118 .0,
119 cancellable.as_ref().to_glib_none().0,
120 &mut error,
121 );
122 if error.is_null() {
123 Ok(from_glib_full(res))
124 } else {
125 Err(from_glib_full(error))
126 }
127 }
128 }
129 }
130
131 fn parent_next_files_async<R: FnOnce(Result<glib::List<FileInfo>, glib::Error>) + 'static>(
132 &self,
133 num_files: i32,
134 priority: glib::Priority,
135 cancellable: Option<&Cancellable>,
136 callback: R,
137 ) {
138 unsafe {
139 let main_context = glib::MainContext::ref_thread_default();
140 let is_main_context_owner = main_context.is_owner();
141 let has_acquired_main_context = (!is_main_context_owner)
142 .then(|| main_context.acquire().ok())
143 .flatten();
144 assert!(
145 is_main_context_owner || has_acquired_main_context.is_some(),
146 "Async operations only allowed if the thread is owning the MainContext"
147 );
148
149 let data = Self::type_data();
150 let parent_class = data.as_ref().parent_class() as *const ffi::GFileEnumeratorClass;
151
152 let f = (*parent_class)
153 .next_files_async
154 .expect("No parent class implementation for \"next_files_async\"");
155 let finish = (*parent_class)
156 .next_files_finish
157 .expect("no parent \"next_files_finish\" implementation");
158
159 let user_data: Box<(glib::thread_guard::ThreadGuard<R>, _)> =
160 Box::new((glib::thread_guard::ThreadGuard::new(callback), finish));
161
162 unsafe extern "C" fn next_files_async_trampoline<
163 R: FnOnce(Result<glib::List<FileInfo>, glib::Error>) + 'static,
164 >(
165 source_object_ptr: *mut glib::gobject_ffi::GObject,
166 res: *mut ffi::GAsyncResult,
167 user_data: glib::ffi::gpointer,
168 ) {
169 unsafe {
170 let mut error = std::ptr::null_mut();
171 let cb: Box<(
172 glib::thread_guard::ThreadGuard<R>,
173 fn(
174 *mut ffi::GFileEnumerator,
175 *mut ffi::GAsyncResult,
176 *mut *mut glib::ffi::GError,
177 ) -> *mut glib::ffi::GList,
178 )> = Box::from_raw(user_data as *mut _);
179 let ret = cb.1(source_object_ptr as _, res, &mut error);
180 let result = if error.is_null() {
181 Ok(glib::List::<FileInfo>::from_glib_full(ret))
182 } else {
183 Err(from_glib_full(error))
184 };
185 let cb = cb.0.into_inner();
186 cb(result);
187 }
188 }
189
190 f(
191 self.obj()
192 .unsafe_cast_ref::<FileEnumerator>()
193 .to_glib_none()
194 .0,
195 num_files,
196 priority.into_glib(),
197 cancellable.to_glib_none().0,
198 Some(next_files_async_trampoline::<R>),
199 Box::into_raw(user_data) as *mut _,
200 );
201 }
202 }
203
204 fn parent_next_files_future(
205 &self,
206 num_files: i32,
207 priority: glib::Priority,
208 ) -> Pin<
209 Box<dyn std::future::Future<Output = Result<glib::List<FileInfo>, glib::Error>> + 'static>,
210 > {
211 Box::pin(GioFuture::new(
212 &self.ref_counted(),
213 move |imp, cancellable, send| {
214 imp.parent_next_files_async(num_files, priority, Some(cancellable), move |res| {
215 send.resolve(res);
216 });
217 },
218 ))
219 }
220
221 fn parent_close(&self, cancellable: Option<&Cancellable>) -> (bool, Option<glib::Error>) {
222 unsafe {
223 let data = Self::type_data();
224 let parent_class = data.as_ref().parent_class() as *const ffi::GFileEnumeratorClass;
225
226 let f = (*parent_class)
227 .close_fn
228 .expect("No parent class implementation for \"close_fn\"");
229
230 let mut error = std::ptr::null_mut();
231 let is_ok = f(
232 self.obj()
233 .unsafe_cast_ref::<FileEnumerator>()
234 .to_glib_none()
235 .0,
236 cancellable.as_ref().to_glib_none().0,
237 &mut error,
238 );
239 (from_glib(is_ok), from_glib_full(error))
240 }
241 }
242
243 fn parent_close_async<R: FnOnce(Result<(), glib::Error>) + 'static>(
244 &self,
245 io_priority: glib::Priority,
246 cancellable: Option<&Cancellable>,
247 callback: R,
248 ) {
249 unsafe {
250 let main_context = glib::MainContext::ref_thread_default();
251 let is_main_context_owner = main_context.is_owner();
252 let has_acquired_main_context = (!is_main_context_owner)
253 .then(|| main_context.acquire().ok())
254 .flatten();
255 assert!(
256 is_main_context_owner || has_acquired_main_context.is_some(),
257 "Async operations only allowed if the thread is owning the MainContext"
258 );
259
260 let data = Self::type_data();
261 let parent_class = data.as_ref().parent_class() as *mut ffi::GFileEnumeratorClass;
262 let f = (*parent_class)
263 .close_async
264 .expect("no parent \"close_async\" implementation");
265 let finish = (*parent_class)
266 .close_finish
267 .expect("no parent \"close_finish\" implementation");
268
269 let user_data: Box<(glib::thread_guard::ThreadGuard<R>, _)> =
270 Box::new((glib::thread_guard::ThreadGuard::new(callback), finish));
271
272 unsafe extern "C" fn close_async_trampoline<
273 R: FnOnce(Result<(), glib::Error>) + 'static,
274 >(
275 source_object_ptr: *mut glib::gobject_ffi::GObject,
276 res: *mut ffi::GAsyncResult,
277 user_data: glib::ffi::gpointer,
278 ) {
279 unsafe {
280 let mut error = std::ptr::null_mut();
281 let cb: Box<(
282 glib::thread_guard::ThreadGuard<R>,
283 fn(
284 *mut ffi::GFileEnumerator,
285 *mut ffi::GAsyncResult,
286 *mut *mut glib::ffi::GError,
287 ) -> glib::ffi::gboolean,
288 )> = Box::from_raw(user_data as *mut _);
289 cb.1(source_object_ptr as _, res, &mut error);
290 let result = if error.is_null() {
291 Ok(())
292 } else {
293 Err(from_glib_full(error))
294 };
295 let cb = cb.0.into_inner();
296 cb(result);
297 }
298 }
299
300 f(
301 self.obj()
302 .unsafe_cast_ref::<FileEnumerator>()
303 .to_glib_none()
304 .0,
305 io_priority.into_glib(),
306 cancellable.to_glib_none().0,
307 Some(close_async_trampoline::<R>),
308 Box::into_raw(user_data) as *mut _,
309 );
310 }
311 }
312
313 fn parent_close_future(
314 &self,
315 io_priority: glib::Priority,
316 ) -> Pin<Box<dyn Future<Output = Result<(), glib::Error>> + 'static>> {
317 Box::pin(GioFuture::new(
318 &self.ref_counted(),
319 move |imp, cancellable, send| {
320 imp.parent_close_async(io_priority, Some(cancellable), move |res| {
321 send.resolve(res);
322 });
323 },
324 ))
325 }
326}
327
328impl<T: FileEnumeratorImpl> FileEnumeratorImplExt for T {}
329
330unsafe impl<T: FileEnumeratorImpl> IsSubclassable<T> for FileEnumerator {
332 fn class_init(class: &mut ::glib::Class<Self>) {
333 Self::parent_class_init::<T>(class);
334
335 let klass = class.as_mut();
336 klass.next_file = Some(next_file::<T>);
337 klass.close_fn = Some(close_fn::<T>);
338 klass.next_files_async = Some(next_files_async::<T>);
339 klass.next_files_finish = Some(next_files_finish);
340 klass.close_async = Some(close_async::<T>);
341 klass.close_finish = Some(close_finish);
342 }
343}
344
345unsafe extern "C" fn next_file<T: FileEnumeratorImpl>(
346 enumerator: *mut ffi::GFileEnumerator,
347 cancellable: *mut ffi::GCancellable,
348 error: *mut *mut glib::ffi::GError,
349) -> *mut ffi::GFileInfo {
350 unsafe {
351 let instance = &*(enumerator as *mut T::Instance);
352 let imp = instance.imp();
353 let cancellable = Option::<Cancellable>::from_glib_none(cancellable);
354
355 let res = imp.next_file(cancellable.as_ref());
356
357 match res {
358 Ok(fileinfo) => fileinfo.to_glib_full(),
359 Err(err) => {
360 if !error.is_null() {
361 *error = err.to_glib_full()
362 }
363 std::ptr::null_mut()
364 }
365 }
366 }
367}
368
369unsafe extern "C" fn next_files_async<T: FileEnumeratorImpl>(
370 enumerator: *mut ffi::GFileEnumerator,
371 num_files: i32,
372 priority: i32,
373 cancellable: *mut ffi::GCancellable,
374 callback: ffi::GAsyncReadyCallback,
375 user_data: glib::ffi::gpointer,
376) {
377 unsafe {
378 let instance = &*(enumerator as *mut T::Instance);
379 let imp = instance.imp();
380 let wrap: FileEnumerator = from_glib_none(enumerator);
381 let cancellable: Option<Cancellable> = from_glib_none(cancellable);
382
383 let closure = move |task: LocalTask<glib::ValueArray>,
385 source_object: Option<&glib::Object>| {
386 let result: *mut ffi::GAsyncResult = task.upcast_ref::<AsyncResult>().to_glib_none().0;
387 let source_object: *mut glib::gobject_ffi::GObject = source_object.to_glib_none().0;
388 callback.unwrap()(source_object, result, user_data)
389 };
390
391 let t = LocalTask::new(
392 Some(wrap.upcast_ref::<glib::Object>()),
393 cancellable.as_ref(),
394 closure,
395 );
396
397 glib::MainContext::ref_thread_default().spawn_local(async move {
399 let res = imp.next_files_future(num_files, from_glib(priority)).await;
401
402 t.return_result(res.map(|files| {
404 let values: Vec<glib::Value> = files
405 .into_iter()
406 .map(|file_info| file_info.to_value())
407 .collect();
408 glib::ValueArray::from_values(values)
409 }));
410 });
411 }
412}
413
414unsafe extern "C" fn next_files_finish(
415 _enumerator: *mut ffi::GFileEnumerator,
416 res_ptr: *mut ffi::GAsyncResult,
417 error_ptr: *mut *mut glib::ffi::GError,
418) -> *mut glib::ffi::GList {
419 unsafe {
420 let res: AsyncResult = from_glib_none(res_ptr);
421 let t = res.downcast::<LocalTask<glib::ValueArray>>().unwrap();
422 let ret = t.propagate();
423 match ret {
424 Ok(files) => {
425 let files = files
426 .iter()
427 .map(|v| <FileInfo as glib::value::FromValue>::from_value(v))
428 .collect::<Vec<FileInfo>>();
429 files.to_glib_full()
430 }
431 Err(e) => {
432 if !error_ptr.is_null() {
433 *error_ptr = e.into_glib_ptr();
434 }
435 std::ptr::null_mut()
436 }
437 }
438 }
439}
440
441unsafe extern "C" fn close_fn<T: FileEnumeratorImpl>(
442 enumerator: *mut ffi::GFileEnumerator,
443 cancellable: *mut ffi::GCancellable,
444 error: *mut *mut glib::ffi::GError,
445) -> glib::ffi::gboolean {
446 unsafe {
447 let instance = &*(enumerator as *mut T::Instance);
448 let imp = instance.imp();
449 let cancellable = Option::<Cancellable>::from_glib_none(cancellable);
450
451 let res = imp.close(cancellable.as_ref());
452
453 if !error.is_null() {
454 *error = res.1.to_glib_full()
455 }
456
457 res.0.into_glib()
458 }
459}
460
461unsafe extern "C" fn close_async<T: FileEnumeratorImpl>(
462 enumerator: *mut ffi::GFileEnumerator,
463 priority: i32,
464 cancellable: *mut ffi::GCancellable,
465 callback: ffi::GAsyncReadyCallback,
466 user_data: glib::ffi::gpointer,
467) {
468 unsafe {
469 let instance = &*(enumerator as *mut T::Instance);
470 let imp = instance.imp();
471 let wrap: FileEnumerator = from_glib_none(enumerator);
472 let cancellable: Option<Cancellable> = from_glib_none(cancellable);
473
474 let closure = move |task: LocalTask<bool>, source_object: Option<&glib::Object>| {
476 let result: *mut ffi::GAsyncResult = task.upcast_ref::<AsyncResult>().to_glib_none().0;
477 let source_object: *mut glib::gobject_ffi::GObject = source_object.to_glib_none().0;
478 callback.unwrap()(source_object, result, user_data)
479 };
480
481 let t = LocalTask::new(
482 Some(wrap.upcast_ref::<glib::Object>()),
483 cancellable.as_ref(),
484 closure,
485 );
486
487 glib::MainContext::ref_thread_default().spawn_local(async move {
489 let res = imp.close_future(from_glib(priority)).await;
491
492 t.return_result(res.map(|_t| true));
494 });
495 }
496}
497
498unsafe extern "C" fn close_finish(
499 _enumerator: *mut ffi::GFileEnumerator,
500 res_ptr: *mut ffi::GAsyncResult,
501 error_ptr: *mut *mut glib::ffi::GError,
502) -> glib::ffi::gboolean {
503 unsafe {
504 let res: AsyncResult = from_glib_none(res_ptr);
505 let t = res.downcast::<LocalTask<bool>>().unwrap();
506 match t.propagate() {
507 Ok(_) => glib::ffi::GTRUE,
508 Err(e) => {
509 if !error_ptr.is_null() {
510 *error_ptr = e.into_glib_ptr();
511 }
512 glib::ffi::GFALSE
513 }
514 }
515 }
516}
517
518#[cfg(test)]
519mod tests {
520 use super::*;
524
525 mod imp {
527 use std::cell::Cell;
528
529 use super::*;
530
531 #[derive(Default)]
532 pub struct MyFileEnumerator(Cell<i32>);
533
534 #[glib::object_subclass]
535 impl ObjectSubclass for MyFileEnumerator {
536 const NAME: &'static str = "MyFileEnumerator";
537 type Type = super::MyFileEnumerator;
538 type ParentType = FileEnumerator;
539 }
540
541 impl ObjectImpl for MyFileEnumerator {
542 fn dispose(&self) {
543 let _ = self.obj().close(Cancellable::NONE);
544 }
545 }
546
547 impl FileEnumeratorImpl for MyFileEnumerator {
549 fn next_file(
550 &self,
551 cancellable: Option<&Cancellable>,
552 ) -> Result<Option<FileInfo>, glib::Error> {
553 if cancellable.is_some_and(|c| c.is_cancelled()) {
554 Err(glib::Error::new::<IOErrorEnum>(
555 IOErrorEnum::Cancelled,
556 "Operation was cancelled",
557 ))
558 } else {
559 match self.0.get() {
560 -1 => Err(glib::Error::new::<IOErrorEnum>(
561 IOErrorEnum::Closed,
562 "Enumerator is closed",
563 )),
564 i if i < 3 => {
565 let file_info = FileInfo::new();
566 file_info.set_display_name(&format!("file{i}"));
567 self.0.set(i + 1);
568 Ok(Some(file_info))
569 }
570 _ => Ok(None),
571 }
572 }
573 }
574
575 fn next_files_future(
576 &self,
577 num_files: i32,
578 _priority: glib::Priority,
579 ) -> std::pin::Pin<
580 Box<
581 dyn std::future::Future<Output = Result<glib::List<FileInfo>, glib::Error>>
582 + 'static,
583 >,
584 > {
585 Box::pin(GioFuture::new(
586 &self.ref_counted(),
587 move |self_, cancellable, send| {
588 let mut res: Result<glib::List<FileInfo>, glib::Error> =
589 Ok(glib::List::new());
590 for _ in 0..num_files {
591 match self_.next_file(Some(cancellable)) {
592 Ok(Some(fi)) => res.as_mut().unwrap().push_back(fi),
593 Ok(None) => break,
594 Err(e) => {
595 res = Err(e);
596 break;
597 }
598 }
599 }
600 send.resolve(res);
601 },
602 ))
603 }
604
605 fn close(&self, cancellable: Option<&Cancellable>) -> (bool, Option<glib::Error>) {
606 if cancellable.is_some_and(|c| c.is_cancelled()) {
607 (
608 false,
609 Some(glib::Error::new::<IOErrorEnum>(
610 IOErrorEnum::Cancelled,
611 "Operation was cancelled",
612 )),
613 )
614 } else {
615 self.0.set(-1);
616 (true, None)
617 }
618 }
619
620 fn close_future(
621 &self,
622 _priority: glib::Priority,
623 ) -> std::pin::Pin<
624 Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>,
625 > {
626 Box::pin(GioFuture::new(
627 &self.ref_counted(),
628 move |self_, cancellable, send| {
629 let (is_ok, error) = self_.close(Some(cancellable));
630 debug_assert_eq!(!is_ok, error.is_some());
631 let res = if is_ok { Ok(()) } else { Err(error.unwrap()) };
632 send.resolve(res);
633 },
634 ))
635 }
636 }
637
638 #[derive(Default)]
639 pub struct MyCustomFileEnumerator;
640
641 #[glib::object_subclass]
642 impl ObjectSubclass for MyCustomFileEnumerator {
643 const NAME: &'static str = "MyCustomFileEnumerator";
644 type Type = super::MyCustomFileEnumerator;
645 type ParentType = super::MyFileEnumerator;
646 }
647
648 impl ObjectImpl for MyCustomFileEnumerator {}
649
650 impl FileEnumeratorImpl for MyCustomFileEnumerator {}
652
653 impl MyFileEnumeratorImpl for MyCustomFileEnumerator {}
654 }
655
656 glib::wrapper! {
657 pub struct MyFileEnumerator(ObjectSubclass<imp::MyFileEnumerator>) @extends FileEnumerator;
658 }
659
660 pub trait MyFileEnumeratorImpl:
661 ObjectImpl + ObjectSubclass<Type: IsA<MyFileEnumerator> + IsA<FileEnumerator>>
662 {
663 }
664
665 unsafe impl<T: MyFileEnumeratorImpl + FileEnumeratorImpl> IsSubclassable<T> for MyFileEnumerator {}
667
668 glib::wrapper! {
669 pub struct MyCustomFileEnumerator(ObjectSubclass<imp::MyCustomFileEnumerator>) @extends MyFileEnumerator, FileEnumerator;
670 }
671
672 #[test]
673 fn file_enumerator_next_file() {
674 let my_custom_file_enumerator = glib::Object::new::<MyCustomFileEnumerator>();
676 let res = my_custom_file_enumerator.next_file(Cancellable::NONE);
677 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
678 let filename = res.unwrap().unwrap().display_name();
679
680 let my_file_enumerator = glib::Object::new::<MyFileEnumerator>();
682 let res = my_file_enumerator.next_file(Cancellable::NONE);
683 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
684 let expected = res.unwrap().unwrap().display_name();
685
686 assert_eq!(filename, expected);
688
689 for res in my_custom_file_enumerator.upcast::<FileEnumerator>() {
691 assert!(res.as_ref().is_ok());
692 let filename = res.unwrap().display_name();
693
694 let res = my_file_enumerator.next_file(Cancellable::NONE);
695 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
696 let expected = res.unwrap().unwrap().display_name();
697
698 assert_eq!(filename, expected);
700 }
701 }
702
703 #[test]
704 fn file_enumerator_next_files_future() {
705 let _ = glib::MainContext::new().with_thread_default(|| {
707 let my_custom_file_enumerator = glib::Object::new::<MyCustomFileEnumerator>();
709 let res = glib::MainContext::ref_thread_default()
710 .block_on(my_custom_file_enumerator.next_files_future(1, glib::Priority::DEFAULT));
711 assert!(res.as_ref().is_ok_and(|res| res.len() == 1));
712 let filename = res.unwrap().first().unwrap().display_name();
713
714 let my_file_enumerator = glib::Object::new::<MyFileEnumerator>();
716 let res = glib::MainContext::ref_thread_default()
717 .block_on(my_file_enumerator.next_files_future(1, glib::Priority::DEFAULT));
718 assert!(res.as_ref().is_ok_and(|res| res.len() == 1));
719 let expected = res.unwrap().first().unwrap().display_name();
720
721 assert_eq!(filename, expected);
723
724 let res = glib::MainContext::ref_thread_default()
726 .block_on(my_custom_file_enumerator.next_files_future(10, glib::Priority::DEFAULT));
727 assert!(res.as_ref().is_ok());
728 let filenames = res
729 .unwrap()
730 .into_iter()
731 .map(|fi| fi.display_name())
732 .collect::<Vec<_>>();
733
734 let res = glib::MainContext::ref_thread_default()
735 .block_on(my_file_enumerator.next_files_future(10, glib::Priority::DEFAULT));
736 assert!(res.as_ref().is_ok());
737 let expected = res
738 .unwrap()
739 .into_iter()
740 .map(|fi| fi.display_name())
741 .collect::<Vec<_>>();
742
743 assert_eq!(filenames, expected);
745 });
746 }
747
748 #[test]
749 fn file_enumerator_close() {
750 let my_custom_file_enumerator = glib::Object::new::<MyCustomFileEnumerator>();
752 let res = my_custom_file_enumerator.next_file(Cancellable::NONE);
753 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
754 let filename = res.unwrap().unwrap().display_name();
755
756 let my_file_enumerator = glib::Object::new::<MyFileEnumerator>();
758 let res = my_file_enumerator.next_file(Cancellable::NONE);
759 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
760 let expected = res.unwrap().unwrap().display_name();
761
762 assert_eq!(filename, expected);
764
765 let res = my_custom_file_enumerator.close(Cancellable::NONE);
767 assert_eq!(res.1, None);
768 let closed = res.0;
769
770 let res = my_file_enumerator.close(Cancellable::NONE);
772 assert_eq!(res.1, None);
773 let expected = res.0;
774
775 assert_eq!(closed, expected);
777
778 let res = my_custom_file_enumerator.next_file(Cancellable::NONE);
780 assert!(res.is_err());
781 let err = res.unwrap_err();
782
783 let res = my_file_enumerator.next_file(Cancellable::NONE);
785 assert!(res.is_err());
786 let expected = res.unwrap_err();
787
788 assert_eq!(err.domain(), expected.domain());
790 assert!(err.matches::<IOErrorEnum>(IOErrorEnum::Closed));
791 assert!(expected.matches::<IOErrorEnum>(IOErrorEnum::Closed));
792 assert_eq!(err.message(), expected.message());
793 }
794
795 #[test]
796 fn file_enumerator_close_future() {
797 let _ = glib::MainContext::new().with_thread_default(|| {
799 let my_custom_file_enumerator = glib::Object::new::<MyCustomFileEnumerator>();
801 let res = glib::MainContext::ref_thread_default()
802 .block_on(my_custom_file_enumerator.next_files_future(1, glib::Priority::DEFAULT));
803 assert!(res.as_ref().is_ok_and(|res| res.len() == 1));
804 let filename = res.unwrap().first().unwrap().display_name();
805
806 let my_file_enumerator = glib::Object::new::<MyFileEnumerator>();
808 let res = glib::MainContext::ref_thread_default()
809 .block_on(my_file_enumerator.next_files_future(1, glib::Priority::DEFAULT));
810 assert!(res.as_ref().is_ok_and(|res| res.len() == 1));
811 let expected = res.unwrap().first().unwrap().display_name();
812
813 assert_eq!(filename, expected);
815
816 let res = glib::MainContext::ref_thread_default()
818 .block_on(my_custom_file_enumerator.close_future(glib::Priority::DEFAULT));
819 assert!(res.is_ok());
820 let closed = true;
821
822 let res = glib::MainContext::ref_thread_default()
824 .block_on(my_file_enumerator.close_future(glib::Priority::DEFAULT));
825 assert!(res.is_ok());
826 let expected = true;
827
828 assert_eq!(closed, expected);
830
831 let res = glib::MainContext::ref_thread_default()
833 .block_on(my_custom_file_enumerator.next_files_future(1, glib::Priority::DEFAULT));
834 assert!(res.is_err());
835 let err = res.unwrap_err();
836
837 let res = glib::MainContext::ref_thread_default()
839 .block_on(my_file_enumerator.next_files_future(1, glib::Priority::DEFAULT));
840 assert!(res.is_err());
841 let expected = res.unwrap_err();
842
843 assert_eq!(err.domain(), expected.domain());
845 assert!(err.matches::<IOErrorEnum>(IOErrorEnum::Closed));
846 assert!(expected.matches::<IOErrorEnum>(IOErrorEnum::Closed));
847 assert_eq!(err.message(), expected.message());
848 });
849 }
850
851 #[test]
852 fn file_enumerator_cancel() {
853 let my_custom_file_enumerator = glib::Object::new::<MyCustomFileEnumerator>();
855 let res = my_custom_file_enumerator.next_file(Cancellable::NONE);
856 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
857 let filename = res.unwrap().unwrap().display_name();
858
859 let my_file_enumerator = glib::Object::new::<MyFileEnumerator>();
861 let res = my_file_enumerator.next_file(Cancellable::NONE);
862 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
863 let expected = res.unwrap().unwrap().display_name();
864
865 assert_eq!(filename, expected);
867
868 let cancellable = Cancellable::new();
870 cancellable.cancel();
871 let res = my_custom_file_enumerator.next_file(Some(&cancellable));
872 assert!(res.as_ref().is_err());
873 let err = res.unwrap_err();
874
875 let cancellable = Cancellable::new();
877 cancellable.cancel();
878 let res = my_file_enumerator.next_file(Some(&cancellable));
879 assert!(res.as_ref().is_err());
880 let expected = res.unwrap_err();
881
882 assert_eq!(err.domain(), expected.domain());
884 assert!(err.matches::<IOErrorEnum>(IOErrorEnum::Cancelled));
885 assert!(expected.matches::<IOErrorEnum>(IOErrorEnum::Cancelled));
886 assert_eq!(err.message(), expected.message());
887
888 let cancellable = Cancellable::new();
890 cancellable.cancel();
891 let res = my_custom_file_enumerator.close(Some(&cancellable));
892 assert!(res.1.is_some());
893 let err = res.1.unwrap();
894
895 let cancellable = Cancellable::new();
897 cancellable.cancel();
898 let res = my_file_enumerator.close(Some(&cancellable));
899 assert!(res.1.is_some());
900 let expected = res.1.unwrap();
901
902 assert_eq!(err.domain(), expected.domain());
904 assert!(err.matches::<IOErrorEnum>(IOErrorEnum::Cancelled));
905 assert!(expected.matches::<IOErrorEnum>(IOErrorEnum::Cancelled));
906 assert_eq!(err.message(), expected.message());
907 }
908}