1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use std::{cell::RefCell, pin::Pin, ptr};

use glib::translate::{from_glib_full, IntoGlib, ToGlibPtr};

use crate::{prelude::*, FileLoader};

impl FileLoader {
    #[doc(alias = "gtk_source_file_loader_load_async")]
    pub fn load_async_with_callback<
        P: FnMut(i64, i64) + Send + 'static,
        Q: FnOnce(Result<(), glib::Error>) + Send + 'static,
    >(
        &self,
        io_priority: glib::Priority,
        cancellable: Option<&impl IsA<gio::Cancellable>>,
        progress_callback: P,
        callback: Q,
    ) {
        self.load_async_impl(
            io_priority,
            cancellable,
            Some(Box::new(progress_callback)),
            callback,
        )
    }

    #[doc(alias = "gtk_source_file_loader_load_async")]
    pub fn load_async<Q: FnOnce(Result<(), glib::Error>) + Send + 'static>(
        &self,
        io_priority: glib::Priority,
        cancellable: Option<&impl IsA<gio::Cancellable>>,
        callback: Q,
    ) {
        self.load_async_impl(io_priority, cancellable, None, callback)
    }

    fn load_async_impl<Q: FnOnce(Result<(), glib::Error>) + Send + 'static>(
        &self,
        io_priority: glib::Priority,
        cancellable: Option<&impl IsA<gio::Cancellable>>,
        progress_callback: Option<Box<dyn FnMut(i64, i64) + Send>>,
        callback: Q,
    ) {
        let progress_trampoline = if progress_callback.is_some() {
            Some(load_async_progress_trampoline::<Q> as _)
        } else {
            None
        };

        let user_data: Box<(Q, RefCell<Option<Box<dyn FnMut(i64, i64) + Send>>>)> =
            Box::new((callback, RefCell::new(progress_callback)));
        unsafe extern "C" fn load_async_trampoline<
            Q: FnOnce(Result<(), glib::Error>) + Send + 'static,
        >(
            _source_object: *mut glib::gobject_ffi::GObject,
            res: *mut gio::ffi::GAsyncResult,
            user_data: glib::ffi::gpointer,
        ) {
            let mut error = ptr::null_mut();
            ffi::gtk_source_file_loader_load_finish(_source_object as *mut _, res, &mut error);
            let result = if error.is_null() {
                Ok(())
            } else {
                Err(from_glib_full(error))
            };
            let callback: Box<(Q, RefCell<Option<Box<dyn FnMut(i64, i64) + Send>>>)> =
                Box::from_raw(user_data as *mut _);
            callback.0(result);
        }
        unsafe extern "C" fn load_async_progress_trampoline<
            Q: FnOnce(Result<(), glib::Error>) + Send + 'static,
        >(
            current_num_bytes: i64,
            total_num_bytes: i64,
            user_data: glib::ffi::gpointer,
        ) {
            let callback: &(Q, RefCell<Option<Box<dyn FnMut(i64, i64) + Send>>>) =
                &*(user_data as *const _);
            (callback.1.borrow_mut().as_mut().expect("no closure"))(
                current_num_bytes,
                total_num_bytes,
            );
        }

        let user_data = Box::into_raw(user_data) as *mut _;

        unsafe {
            ffi::gtk_source_file_loader_load_async(
                self.to_glib_none().0,
                io_priority.into_glib(),
                cancellable.map(|p| p.as_ref()).to_glib_none().0,
                progress_trampoline,
                user_data,
                None,
                Some(load_async_trampoline::<Q>),
                user_data,
            );
        }
    }

    pub fn load_future(
        &self,
        io_priority: glib::Priority,
    ) -> (
        Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>>,
        Pin<Box<dyn futures_core::stream::Stream<Item = (i64, i64)> + 'static>>,
    ) {
        let (sender, receiver) = futures_channel::mpsc::unbounded();

        let fut = Box::pin(gtk::gio::GioFuture::new(
            self,
            move |obj, cancellable, send| {
                obj.load_async_with_callback(
                    io_priority,
                    Some(cancellable),
                    move |current_num_bytes, total_num_bytes| {
                        let _ = sender.unbounded_send((current_num_bytes, total_num_bytes));
                    },
                    move |res| {
                        send.resolve(res);
                    },
                );
            },
        ));

        (fut, Box::pin(receiver))
    }
}