sourceview5/
file_saver.rs

1use std::{cell::RefCell, pin::Pin, ptr};
2
3use glib::translate::{from_glib_full, IntoGlib, ToGlibPtr};
4
5use crate::{prelude::*, FileSaver};
6
7impl FileSaver {
8    #[doc(alias = "gtk_source_file_saver_save_async")]
9    pub fn save_async<Q: FnOnce(Result<(), glib::Error>) + Send + 'static>(
10        &self,
11        io_priority: glib::Priority,
12        cancellable: Option<&impl IsA<gio::Cancellable>>,
13        callback: Q,
14    ) {
15        self.save_async_impl(io_priority, cancellable, None, callback)
16    }
17
18    #[doc(alias = "gtk_source_file_saver_save_async")]
19    pub fn save_async_with_callback<
20        P: FnMut(i64, i64) + Send + 'static,
21        Q: FnOnce(Result<(), glib::Error>) + Send + 'static,
22    >(
23        &self,
24        io_priority: glib::Priority,
25        cancellable: Option<&impl IsA<gio::Cancellable>>,
26        progress_callback: P,
27        callback: Q,
28    ) {
29        self.save_async_impl(
30            io_priority,
31            cancellable,
32            Some(Box::new(progress_callback)),
33            callback,
34        )
35    }
36
37    fn save_async_impl<Q: FnOnce(Result<(), glib::Error>) + Send + 'static>(
38        &self,
39        io_priority: glib::Priority,
40        cancellable: Option<&impl IsA<gio::Cancellable>>,
41        progress_callback: Option<Box<dyn FnMut(i64, i64) + Send>>,
42        callback: Q,
43    ) {
44        let progress_trampoline = if progress_callback.is_some() {
45            Some(save_async_progress_trampoline::<Q> as _)
46        } else {
47            None
48        };
49
50        let user_data: Box<(Q, RefCell<Option<Box<dyn FnMut(i64, i64) + Send>>>)> = Box::new((
51            callback,
52            RefCell::new(
53                progress_callback.map(|p| -> Box<dyn FnMut(i64, i64) + Send> { Box::new(p) }),
54            ),
55        ));
56        unsafe extern "C" fn save_async_trampoline<
57            Q: FnOnce(Result<(), glib::Error>) + Send + 'static,
58        >(
59            _source_object: *mut glib::gobject_ffi::GObject,
60            res: *mut gio::ffi::GAsyncResult,
61            user_data: glib::ffi::gpointer,
62        ) {
63            let mut error = ptr::null_mut();
64            ffi::gtk_source_file_saver_save_finish(_source_object as *mut _, res, &mut error);
65            let result = if error.is_null() {
66                Ok(())
67            } else {
68                Err(from_glib_full(error))
69            };
70            let callback: Box<(Q, RefCell<Option<Box<dyn FnMut(i64, i64) + Send>>>)> =
71                Box::from_raw(user_data as *mut _);
72            callback.0(result);
73        }
74        unsafe extern "C" fn save_async_progress_trampoline<
75            Q: FnOnce(Result<(), glib::Error>) + Send + 'static,
76        >(
77            current_num_bytes: i64,
78            total_num_bytes: i64,
79            user_data: glib::ffi::gpointer,
80        ) {
81            let callback: &(Q, RefCell<Option<Box<dyn FnMut(i64, i64) + Send>>>) =
82                &*(user_data as *const _);
83            (callback.1.borrow_mut().as_mut().expect("no closure"))(
84                current_num_bytes,
85                total_num_bytes,
86            );
87        }
88
89        let user_data = Box::into_raw(user_data) as *mut _;
90
91        unsafe {
92            ffi::gtk_source_file_saver_save_async(
93                self.to_glib_none().0,
94                io_priority.into_glib(),
95                cancellable.map(|p| p.as_ref()).to_glib_none().0,
96                progress_trampoline,
97                user_data,
98                None,
99                Some(save_async_trampoline::<Q>),
100                user_data,
101            );
102        }
103    }
104
105    pub fn save_future(
106        &self,
107        io_priority: glib::Priority,
108    ) -> (
109        Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>>,
110        Pin<Box<dyn futures_core::stream::Stream<Item = (i64, i64)> + 'static>>,
111    ) {
112        let (sender, receiver) = futures_channel::mpsc::unbounded();
113
114        let fut = Box::pin(gtk::gio::GioFuture::new(
115            self,
116            move |obj, cancellable, send| {
117                obj.save_async_with_callback(
118                    io_priority,
119                    Some(cancellable),
120                    move |current_num_bytes, total_num_bytes| {
121                        let _ = sender.unbounded_send((current_num_bytes, total_num_bytes));
122                    },
123                    move |res| {
124                        send.resolve(res);
125                    },
126                );
127            },
128        ));
129
130        (fut, Box::pin(receiver))
131    }
132}