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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use std::{cell::RefCell, pin::Pin, ptr};

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

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

impl FileSaver {
    /// Saves asynchronously the buffer into the file.
    ///
    /// See the `Gio::AsyncResult` documentation to know how to use this function.
    /// ## `io_priority`
    /// the I/O priority of the request. E.g. `G_PRIORITY_LOW`,
    ///   `G_PRIORITY_DEFAULT` or `G_PRIORITY_HIGH`.
    /// ## `cancellable`
    /// optional #GCancellable object, [`None`] to ignore.
    /// ## `progress_callback`
    /// function to call back with
    ///   progress information, or [`None`] if progress information is not needed.
    /// ## `progress_callback_data`
    /// user data to pass to @progress_callback.
    /// ## `progress_callback_notify`
    /// function to call on
    ///   @progress_callback_data when the @progress_callback is no longer needed, or
    ///   [`None`].
    /// ## `callback`
    /// a #GAsyncReadyCallback to call when the request is
    ///   satisfied.
    #[doc(alias = "gtk_source_file_saver_save_async")]
    pub fn save_async<Q: FnOnce(Result<(), glib::Error>) + Send + 'static>(
        &self,
        io_priority: glib::Priority,
        cancellable: Option<&impl IsA<gio::Cancellable>>,
        callback: Q,
    ) {
        self.save_async_impl(io_priority, cancellable, None, callback)
    }

    #[doc(alias = "gtk_source_file_saver_save_async")]
    pub fn save_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.save_async_impl(
            io_priority,
            cancellable,
            Some(Box::new(progress_callback)),
            callback,
        )
    }

    fn save_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(save_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.map(|p| -> Box<dyn FnMut(i64, i64) + Send> { Box::new(p) }),
            ),
        ));
        unsafe extern "C" fn save_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_saver_save_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 save_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_saver_save_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(save_async_trampoline::<Q>),
                user_data,
            );
        }
    }

    pub fn save_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.save_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))
    }
}