gio/
file.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{cell::RefCell, mem, pin::Pin, ptr};
4
5use glib::{prelude::*, translate::*};
6
7#[cfg(feature = "v2_74")]
8use crate::FileIOStream;
9use crate::{ffi, Cancellable, File, FileCreateFlags, FileEnumerator, FileQueryInfoFlags};
10
11impl File {
12    /// Asynchronously opens a file in the preferred directory for temporary files
13    ///  (as returned by g_get_tmp_dir()) as g_file_new_tmp().
14    ///
15    /// @tmpl should be a string in the GLib file name encoding
16    /// containing a sequence of six 'X' characters, and containing no
17    /// directory components. If it is [`None`], a default template is used.
18    /// ## `tmpl`
19    /// Template for the file
20    ///   name, as in g_file_open_tmp(), or [`None`] for a default template
21    /// ## `io_priority`
22    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
23    /// ## `cancellable`
24    /// optional #GCancellable object, [`None`] to ignore
25    /// ## `callback`
26    /// a #GAsyncReadyCallback to call when the request is done
27    #[cfg(feature = "v2_74")]
28    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
29    #[doc(alias = "g_file_new_tmp_async")]
30    pub fn new_tmp_async<P: FnOnce(Result<(File, FileIOStream), glib::Error>) + 'static>(
31        tmpl: Option<impl AsRef<std::path::Path>>,
32        io_priority: glib::Priority,
33        cancellable: Option<&impl IsA<Cancellable>>,
34        callback: P,
35    ) {
36        let main_context = glib::MainContext::ref_thread_default();
37        let is_main_context_owner = main_context.is_owner();
38        let has_acquired_main_context = (!is_main_context_owner)
39            .then(|| main_context.acquire().ok())
40            .flatten();
41        assert!(
42            is_main_context_owner || has_acquired_main_context.is_some(),
43            "Async operations only allowed if the thread is owning the MainContext"
44        );
45
46        let user_data: Box<glib::thread_guard::ThreadGuard<P>> =
47            Box::new(glib::thread_guard::ThreadGuard::new(callback));
48        unsafe extern "C" fn new_tmp_async_trampoline<
49            P: FnOnce(Result<(File, FileIOStream), glib::Error>) + 'static,
50        >(
51            _source_object: *mut glib::gobject_ffi::GObject,
52            res: *mut crate::ffi::GAsyncResult,
53            user_data: glib::ffi::gpointer,
54        ) {
55            let mut error = ptr::null_mut();
56            let mut iostream = ptr::null_mut();
57            let ret = ffi::g_file_new_tmp_finish(res, &mut iostream, &mut error);
58            let result = if error.is_null() {
59                Ok((from_glib_full(ret), from_glib_full(iostream)))
60            } else {
61                Err(from_glib_full(error))
62            };
63            let callback: Box<glib::thread_guard::ThreadGuard<P>> =
64                Box::from_raw(user_data as *mut _);
65            let callback: P = callback.into_inner();
66            callback(result);
67        }
68        let callback = new_tmp_async_trampoline::<P>;
69        unsafe {
70            ffi::g_file_new_tmp_async(
71                tmpl.as_ref().map(|p| p.as_ref()).to_glib_none().0,
72                io_priority.into_glib(),
73                cancellable.map(|p| p.as_ref()).to_glib_none().0,
74                Some(callback),
75                Box::into_raw(user_data) as *mut _,
76            );
77        }
78    }
79
80    #[cfg(feature = "v2_74")]
81    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
82    pub fn new_tmp_future(
83        tmpl: Option<impl AsRef<std::path::Path>>,
84        io_priority: glib::Priority,
85    ) -> Pin<
86        Box<dyn std::future::Future<Output = Result<(File, FileIOStream), glib::Error>> + 'static>,
87    > {
88        let tmpl = tmpl.map(|tmpl| tmpl.as_ref().to_owned());
89        Box::pin(crate::GioFuture::new(
90            &(),
91            move |_obj, cancellable, send| {
92                Self::new_tmp_async(
93                    tmpl.as_ref()
94                        .map(<std::path::PathBuf as std::borrow::Borrow<std::path::Path>>::borrow),
95                    io_priority,
96                    Some(cancellable),
97                    move |res| {
98                        send.resolve(res);
99                    },
100                );
101            },
102        ))
103    }
104
105    /// Asynchronously creates a directory in the preferred directory for
106    /// temporary files (as returned by g_get_tmp_dir()) as g_dir_make_tmp().
107    ///
108    /// @tmpl should be a string in the GLib file name encoding
109    /// containing a sequence of six 'X' characters, and containing no
110    /// directory components. If it is [`None`], a default template is used.
111    /// ## `tmpl`
112    /// Template for the file
113    ///   name, as in g_dir_make_tmp(), or [`None`] for a default template
114    /// ## `io_priority`
115    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
116    /// ## `cancellable`
117    /// optional #GCancellable object, [`None`] to ignore
118    /// ## `callback`
119    /// a #GAsyncReadyCallback to call when the request is done
120    #[cfg(feature = "v2_74")]
121    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
122    #[doc(alias = "g_file_new_tmp_dir_async")]
123    pub fn new_tmp_dir_async<P: FnOnce(Result<File, glib::Error>) + 'static>(
124        tmpl: Option<impl AsRef<std::path::Path>>,
125        io_priority: glib::Priority,
126        cancellable: Option<&impl IsA<Cancellable>>,
127        callback: P,
128    ) {
129        let main_context = glib::MainContext::ref_thread_default();
130        let is_main_context_owner = main_context.is_owner();
131        let has_acquired_main_context = (!is_main_context_owner)
132            .then(|| main_context.acquire().ok())
133            .flatten();
134        assert!(
135            is_main_context_owner || has_acquired_main_context.is_some(),
136            "Async operations only allowed if the thread is owning the MainContext"
137        );
138
139        let user_data: Box<glib::thread_guard::ThreadGuard<P>> =
140            Box::new(glib::thread_guard::ThreadGuard::new(callback));
141        unsafe extern "C" fn new_tmp_dir_async_trampoline<
142            P: FnOnce(Result<File, glib::Error>) + 'static,
143        >(
144            _source_object: *mut glib::gobject_ffi::GObject,
145            res: *mut crate::ffi::GAsyncResult,
146            user_data: glib::ffi::gpointer,
147        ) {
148            let mut error = ptr::null_mut();
149            let ret = ffi::g_file_new_tmp_dir_finish(res, &mut error);
150            let result = if error.is_null() {
151                Ok(from_glib_full(ret))
152            } else {
153                Err(from_glib_full(error))
154            };
155            let callback: Box<glib::thread_guard::ThreadGuard<P>> =
156                Box::from_raw(user_data as *mut _);
157            let callback: P = callback.into_inner();
158            callback(result);
159        }
160        let callback = new_tmp_dir_async_trampoline::<P>;
161        unsafe {
162            ffi::g_file_new_tmp_dir_async(
163                tmpl.as_ref().map(|p| p.as_ref()).to_glib_none().0,
164                io_priority.into_glib(),
165                cancellable.map(|p| p.as_ref()).to_glib_none().0,
166                Some(callback),
167                Box::into_raw(user_data) as *mut _,
168            );
169        }
170    }
171
172    #[cfg(feature = "v2_74")]
173    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
174    pub fn new_tmp_dir_future(
175        tmpl: Option<impl AsRef<std::path::Path>>,
176        io_priority: glib::Priority,
177    ) -> Pin<Box<dyn std::future::Future<Output = Result<File, glib::Error>> + 'static>> {
178        let tmpl = tmpl.map(|tmpl| tmpl.as_ref().to_owned());
179        Box::pin(crate::GioFuture::new(
180            &(),
181            move |_obj, cancellable, send| {
182                Self::new_tmp_dir_async(
183                    tmpl.as_ref()
184                        .map(<std::path::PathBuf as std::borrow::Borrow<std::path::Path>>::borrow),
185                    io_priority,
186                    Some(cancellable),
187                    move |res| {
188                        send.resolve(res);
189                    },
190                );
191            },
192        ))
193    }
194}
195
196mod sealed {
197    pub trait Sealed {}
198    impl<T: super::IsA<super::File>> Sealed for T {}
199}
200
201pub trait FileExtManual: sealed::Sealed + IsA<File> + Sized {
202    /// Starts an asynchronous replacement of @self with the given
203    /// @contents of @length bytes. @etag will replace the document's
204    /// current entity tag.
205    ///
206    /// When this operation has completed, @callback will be called with
207    /// @user_user data, and the operation can be finalized with
208    /// g_file_replace_contents_finish().
209    ///
210    /// If @cancellable is not [`None`], then the operation can be cancelled by
211    /// triggering the cancellable object from another thread. If the operation
212    /// was cancelled, the error [`IOErrorEnum::Cancelled`][crate::IOErrorEnum::Cancelled] will be returned.
213    ///
214    /// If @make_backup is [`true`], this function will attempt to
215    /// make a backup of @self.
216    ///
217    /// Note that no copy of @contents will be made, so it must stay valid
218    /// until @callback is called. See g_file_replace_contents_bytes_async()
219    /// for a #GBytes version that will automatically hold a reference to the
220    /// contents (without copying) for the duration of the call.
221    /// ## `contents`
222    /// string of contents to replace the file with
223    /// ## `etag`
224    /// a new [entity tag](#entity-tags) for the @self, or [`None`]
225    /// ## `make_backup`
226    /// [`true`] if a backup should be created
227    /// ## `flags`
228    /// a set of #GFileCreateFlags
229    /// ## `cancellable`
230    /// optional #GCancellable object, [`None`] to ignore
231    /// ## `callback`
232    /// a #GAsyncReadyCallback to call when the request is satisfied
233    #[doc(alias = "g_file_replace_contents_async")]
234    fn replace_contents_async<
235        B: AsRef<[u8]> + Send + 'static,
236        R: FnOnce(Result<(B, glib::GString), (B, glib::Error)>) + 'static,
237        C: IsA<Cancellable>,
238    >(
239        &self,
240        contents: B,
241        etag: Option<&str>,
242        make_backup: bool,
243        flags: FileCreateFlags,
244        cancellable: Option<&C>,
245        callback: R,
246    ) {
247        let main_context = glib::MainContext::ref_thread_default();
248        let is_main_context_owner = main_context.is_owner();
249        let has_acquired_main_context = (!is_main_context_owner)
250            .then(|| main_context.acquire().ok())
251            .flatten();
252        assert!(
253            is_main_context_owner || has_acquired_main_context.is_some(),
254            "Async operations only allowed if the thread is owning the MainContext"
255        );
256
257        let etag = etag.to_glib_none();
258        let cancellable = cancellable.map(|c| c.as_ref());
259        let gcancellable = cancellable.to_glib_none();
260        let user_data: Box<(glib::thread_guard::ThreadGuard<R>, B)> =
261            Box::new((glib::thread_guard::ThreadGuard::new(callback), contents));
262        // Need to do this after boxing as the contents pointer might change by moving into the box
263        let (count, contents_ptr) = {
264            let contents = &user_data.1;
265            let slice = contents.as_ref();
266            (slice.len(), slice.as_ptr())
267        };
268        unsafe extern "C" fn replace_contents_async_trampoline<
269            B: AsRef<[u8]> + Send + 'static,
270            R: FnOnce(Result<(B, glib::GString), (B, glib::Error)>) + 'static,
271        >(
272            _source_object: *mut glib::gobject_ffi::GObject,
273            res: *mut ffi::GAsyncResult,
274            user_data: glib::ffi::gpointer,
275        ) {
276            let user_data: Box<(glib::thread_guard::ThreadGuard<R>, B)> =
277                Box::from_raw(user_data as *mut _);
278            let (callback, contents) = *user_data;
279            let callback = callback.into_inner();
280
281            let mut error = ptr::null_mut();
282            let mut new_etag = ptr::null_mut();
283            let _ = ffi::g_file_replace_contents_finish(
284                _source_object as *mut _,
285                res,
286                &mut new_etag,
287                &mut error,
288            );
289            let result = if error.is_null() {
290                Ok((contents, from_glib_full(new_etag)))
291            } else {
292                Err((contents, from_glib_full(error)))
293            };
294            callback(result);
295        }
296        let callback = replace_contents_async_trampoline::<B, R>;
297        unsafe {
298            ffi::g_file_replace_contents_async(
299                self.as_ref().to_glib_none().0,
300                mut_override(contents_ptr),
301                count,
302                etag.0,
303                make_backup.into_glib(),
304                flags.into_glib(),
305                gcancellable.0,
306                Some(callback),
307                Box::into_raw(user_data) as *mut _,
308            );
309        }
310    }
311
312    fn replace_contents_future<B: AsRef<[u8]> + Send + 'static>(
313        &self,
314        contents: B,
315        etag: Option<&str>,
316        make_backup: bool,
317        flags: FileCreateFlags,
318    ) -> Pin<
319        Box<
320            dyn std::future::Future<Output = Result<(B, glib::GString), (B, glib::Error)>>
321                + 'static,
322        >,
323    > {
324        let etag = etag.map(glib::GString::from);
325        Box::pin(crate::GioFuture::new(
326            self,
327            move |obj, cancellable, send| {
328                obj.replace_contents_async(
329                    contents,
330                    etag.as_ref().map(|s| s.as_str()),
331                    make_backup,
332                    flags,
333                    Some(cancellable),
334                    move |res| {
335                        send.resolve(res);
336                    },
337                );
338            },
339        ))
340    }
341
342    #[doc(alias = "g_file_enumerate_children_async")]
343    fn enumerate_children_async<
344        P: IsA<Cancellable>,
345        Q: FnOnce(Result<FileEnumerator, glib::Error>) + 'static,
346    >(
347        &self,
348        attributes: &str,
349        flags: FileQueryInfoFlags,
350        io_priority: glib::Priority,
351        cancellable: Option<&P>,
352        callback: Q,
353    ) {
354        let main_context = glib::MainContext::ref_thread_default();
355        let is_main_context_owner = main_context.is_owner();
356        let has_acquired_main_context = (!is_main_context_owner)
357            .then(|| main_context.acquire().ok())
358            .flatten();
359        assert!(
360            is_main_context_owner || has_acquired_main_context.is_some(),
361            "Async operations only allowed if the thread is owning the MainContext"
362        );
363
364        let user_data: Box<glib::thread_guard::ThreadGuard<Q>> =
365            Box::new(glib::thread_guard::ThreadGuard::new(callback));
366        unsafe extern "C" fn create_async_trampoline<
367            Q: FnOnce(Result<FileEnumerator, glib::Error>) + 'static,
368        >(
369            _source_object: *mut glib::gobject_ffi::GObject,
370            res: *mut crate::ffi::GAsyncResult,
371            user_data: glib::ffi::gpointer,
372        ) {
373            let mut error = ptr::null_mut();
374            let ret =
375                ffi::g_file_enumerate_children_finish(_source_object as *mut _, res, &mut error);
376            let result = if error.is_null() {
377                Ok(from_glib_full(ret))
378            } else {
379                Err(from_glib_full(error))
380            };
381            let callback: Box<glib::thread_guard::ThreadGuard<Q>> =
382                Box::from_raw(user_data as *mut _);
383            let callback = callback.into_inner();
384            callback(result);
385        }
386        let callback = create_async_trampoline::<Q>;
387        unsafe {
388            ffi::g_file_enumerate_children_async(
389                self.as_ref().to_glib_none().0,
390                attributes.to_glib_none().0,
391                flags.into_glib(),
392                io_priority.into_glib(),
393                cancellable.map(|p| p.as_ref()).to_glib_none().0,
394                Some(callback),
395                Box::into_raw(user_data) as *mut _,
396            );
397        }
398    }
399
400    fn enumerate_children_future(
401        &self,
402        attributes: &str,
403        flags: FileQueryInfoFlags,
404        io_priority: glib::Priority,
405    ) -> Pin<Box<dyn std::future::Future<Output = Result<FileEnumerator, glib::Error>> + 'static>>
406    {
407        let attributes = attributes.to_owned();
408        Box::pin(crate::GioFuture::new(
409            self,
410            move |obj, cancellable, send| {
411                obj.enumerate_children_async(
412                    &attributes,
413                    flags,
414                    io_priority,
415                    Some(cancellable),
416                    move |res| {
417                        send.resolve(res);
418                    },
419                );
420            },
421        ))
422    }
423
424    /// Copies the file @self to the location specified by @destination
425    /// asynchronously. For details of the behaviour, see g_file_copy().
426    ///
427    /// If @progress_callback is not [`None`], then that function that will be called
428    /// just like in g_file_copy(). The callback will run in the default main context
429    /// of the thread calling g_file_copy_async() — the same context as @callback is
430    /// run in.
431    ///
432    /// When the operation is finished, @callback will be called. You can then call
433    /// g_file_copy_finish() to get the result of the operation.
434    /// ## `destination`
435    /// destination #GFile
436    /// ## `flags`
437    /// set of #GFileCopyFlags
438    /// ## `io_priority`
439    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
440    /// ## `cancellable`
441    /// optional #GCancellable object,
442    ///   [`None`] to ignore
443    /// ## `progress_callback`
444    ///
445    ///   function to callback with progress information, or [`None`] if
446    ///   progress information is not needed
447    /// ## `progress_callback_data`
448    /// user data to pass to @progress_callback
449    /// ## `callback`
450    /// a #GAsyncReadyCallback
451    ///   to call when the request is satisfied
452    #[doc(alias = "g_file_copy_async")]
453    fn copy_async<Q: FnOnce(Result<(), glib::Error>) + 'static>(
454        &self,
455        destination: &impl IsA<File>,
456        flags: crate::FileCopyFlags,
457        io_priority: glib::Priority,
458        cancellable: Option<&impl IsA<Cancellable>>,
459        progress_callback: Option<Box<dyn FnMut(i64, i64)>>,
460        callback: Q,
461    ) {
462        let main_context = glib::MainContext::ref_thread_default();
463        let is_main_context_owner = main_context.is_owner();
464        let has_acquired_main_context = (!is_main_context_owner)
465            .then(|| main_context.acquire().ok())
466            .flatten();
467        assert!(
468            is_main_context_owner || has_acquired_main_context.is_some(),
469            "Async operations only allowed if the thread is owning the MainContext"
470        );
471
472        let progress_trampoline = if progress_callback.is_some() {
473            Some(copy_async_progress_trampoline::<Q> as _)
474        } else {
475            None
476        };
477
478        let user_data: Box<(
479            glib::thread_guard::ThreadGuard<Q>,
480            RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
481        )> = Box::new((
482            glib::thread_guard::ThreadGuard::new(callback),
483            RefCell::new(progress_callback.map(glib::thread_guard::ThreadGuard::new)),
484        ));
485        unsafe extern "C" fn copy_async_trampoline<Q: FnOnce(Result<(), glib::Error>) + 'static>(
486            _source_object: *mut glib::gobject_ffi::GObject,
487            res: *mut crate::ffi::GAsyncResult,
488            user_data: glib::ffi::gpointer,
489        ) {
490            let mut error = ptr::null_mut();
491            ffi::g_file_copy_finish(_source_object as *mut _, res, &mut error);
492            let result = if error.is_null() {
493                Ok(())
494            } else {
495                Err(from_glib_full(error))
496            };
497            let callback: Box<(
498                glib::thread_guard::ThreadGuard<Q>,
499                RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
500            )> = Box::from_raw(user_data as *mut _);
501            let callback = callback.0.into_inner();
502            callback(result);
503        }
504        unsafe extern "C" fn copy_async_progress_trampoline<
505            Q: FnOnce(Result<(), glib::Error>) + 'static,
506        >(
507            current_num_bytes: i64,
508            total_num_bytes: i64,
509            user_data: glib::ffi::gpointer,
510        ) {
511            let callback: &(
512                glib::thread_guard::ThreadGuard<Q>,
513                RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
514            ) = &*(user_data as *const _);
515            (callback
516                .1
517                .borrow_mut()
518                .as_mut()
519                .expect("no closure")
520                .get_mut())(current_num_bytes, total_num_bytes);
521        }
522
523        let user_data = Box::into_raw(user_data) as *mut _;
524
525        unsafe {
526            ffi::g_file_copy_async(
527                self.as_ref().to_glib_none().0,
528                destination.as_ref().to_glib_none().0,
529                flags.into_glib(),
530                io_priority.into_glib(),
531                cancellable.map(|p| p.as_ref()).to_glib_none().0,
532                progress_trampoline,
533                user_data,
534                Some(copy_async_trampoline::<Q>),
535                user_data,
536            );
537        }
538    }
539
540    fn copy_future(
541        &self,
542        destination: &(impl IsA<File> + Clone + 'static),
543        flags: crate::FileCopyFlags,
544        io_priority: glib::Priority,
545    ) -> (
546        Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>>,
547        Pin<Box<dyn futures_core::stream::Stream<Item = (i64, i64)> + 'static>>,
548    ) {
549        let destination = destination.clone();
550
551        let (sender, receiver) = futures_channel::mpsc::unbounded();
552
553        let fut = Box::pin(crate::GioFuture::new(
554            self,
555            move |obj, cancellable, send| {
556                obj.copy_async(
557                    &destination,
558                    flags,
559                    io_priority,
560                    Some(cancellable),
561                    Some(Box::new(move |current_num_bytes, total_num_bytes| {
562                        let _ = sender.unbounded_send((current_num_bytes, total_num_bytes));
563                    })),
564                    move |res| {
565                        send.resolve(res);
566                    },
567                );
568            },
569        ));
570
571        (fut, Box::pin(receiver))
572    }
573
574    /// Loads the content of the file into memory. The data is always
575    /// zero-terminated, but this is not included in the resultant @length.
576    /// The returned @contents should be freed with g_free() when no longer
577    /// needed.
578    ///
579    /// If @cancellable is not [`None`], then the operation can be cancelled by
580    /// triggering the cancellable object from another thread. If the operation
581    /// was cancelled, the error [`IOErrorEnum::Cancelled`][crate::IOErrorEnum::Cancelled] will be returned.
582    /// ## `cancellable`
583    /// optional #GCancellable object, [`None`] to ignore
584    ///
585    /// # Returns
586    ///
587    /// [`true`] if the @self's contents were successfully loaded.
588    ///   [`false`] if there were errors.
589    ///
590    /// ## `contents`
591    /// a location to place the contents of the file
592    ///
593    /// ## `etag_out`
594    /// a location to place the current entity tag for the file,
595    ///   or [`None`] if the entity tag is not needed
596    #[doc(alias = "g_file_load_contents")]
597    fn load_contents(
598        &self,
599        cancellable: Option<&impl IsA<Cancellable>>,
600    ) -> Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error> {
601        unsafe {
602            let mut contents = std::ptr::null_mut();
603            let mut length = std::mem::MaybeUninit::uninit();
604            let mut etag_out = std::ptr::null_mut();
605            let mut error = std::ptr::null_mut();
606            let is_ok = ffi::g_file_load_contents(
607                self.as_ref().to_glib_none().0,
608                cancellable.map(|p| p.as_ref()).to_glib_none().0,
609                &mut contents,
610                length.as_mut_ptr(),
611                &mut etag_out,
612                &mut error,
613            );
614            debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null());
615            if error.is_null() {
616                Ok((
617                    FromGlibContainer::from_glib_full_num(contents, length.assume_init() as _),
618                    from_glib_full(etag_out),
619                ))
620            } else {
621                Err(from_glib_full(error))
622            }
623        }
624    }
625
626    /// Starts an asynchronous load of the @self's contents.
627    ///
628    /// For more details, see g_file_load_contents() which is
629    /// the synchronous version of this call.
630    ///
631    /// When the load operation has completed, @callback will be called
632    /// with @user data. To finish the operation, call
633    /// g_file_load_contents_finish() with the #GAsyncResult returned by
634    /// the @callback.
635    ///
636    /// If @cancellable is not [`None`], then the operation can be cancelled by
637    /// triggering the cancellable object from another thread. If the operation
638    /// was cancelled, the error [`IOErrorEnum::Cancelled`][crate::IOErrorEnum::Cancelled] will be returned.
639    /// ## `cancellable`
640    /// optional #GCancellable object, [`None`] to ignore
641    /// ## `callback`
642    /// a #GAsyncReadyCallback to call when the request is satisfied
643    #[doc(alias = "g_file_load_contents_async")]
644    fn load_contents_async<
645        P: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
646            + 'static,
647    >(
648        &self,
649        cancellable: Option<&impl IsA<Cancellable>>,
650        callback: P,
651    ) {
652        let main_context = glib::MainContext::ref_thread_default();
653        let is_main_context_owner = main_context.is_owner();
654        let has_acquired_main_context = (!is_main_context_owner)
655            .then(|| main_context.acquire().ok())
656            .flatten();
657        assert!(
658            is_main_context_owner || has_acquired_main_context.is_some(),
659            "Async operations only allowed if the thread is owning the MainContext"
660        );
661
662        let user_data: Box<glib::thread_guard::ThreadGuard<P>> =
663            Box::new(glib::thread_guard::ThreadGuard::new(callback));
664        unsafe extern "C" fn load_contents_async_trampoline<
665            P: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
666                + 'static,
667        >(
668            _source_object: *mut glib::gobject_ffi::GObject,
669            res: *mut crate::ffi::GAsyncResult,
670            user_data: glib::ffi::gpointer,
671        ) {
672            let mut error = std::ptr::null_mut();
673            let mut contents = std::ptr::null_mut();
674            let mut length = std::mem::MaybeUninit::uninit();
675            let mut etag_out = std::ptr::null_mut();
676            let _ = ffi::g_file_load_contents_finish(
677                _source_object as *mut _,
678                res,
679                &mut contents,
680                length.as_mut_ptr(),
681                &mut etag_out,
682                &mut error,
683            );
684            let result = if error.is_null() {
685                Ok((
686                    FromGlibContainer::from_glib_full_num(contents, length.assume_init() as _),
687                    from_glib_full(etag_out),
688                ))
689            } else {
690                Err(from_glib_full(error))
691            };
692            let callback: Box<glib::thread_guard::ThreadGuard<P>> =
693                Box::from_raw(user_data as *mut _);
694            let callback: P = callback.into_inner();
695            callback(result);
696        }
697        let callback = load_contents_async_trampoline::<P>;
698        unsafe {
699            ffi::g_file_load_contents_async(
700                self.as_ref().to_glib_none().0,
701                cancellable.map(|p| p.as_ref()).to_glib_none().0,
702                Some(callback),
703                Box::into_raw(user_data) as *mut _,
704            );
705        }
706    }
707
708    fn load_contents_future(
709        &self,
710    ) -> Pin<
711        Box<
712            dyn std::future::Future<
713                    Output = Result<
714                        (glib::collections::Slice<u8>, Option<glib::GString>),
715                        glib::Error,
716                    >,
717                > + 'static,
718        >,
719    > {
720        Box::pin(crate::GioFuture::new(
721            self,
722            move |obj, cancellable, send| {
723                obj.load_contents_async(Some(cancellable), move |res| {
724                    send.resolve(res);
725                });
726            },
727        ))
728    }
729
730    /// Reads the partial contents of a file. A #GFileReadMoreCallback should
731    /// be used to stop reading from the file when appropriate, else this
732    /// function will behave exactly as g_file_load_contents_async(). This
733    /// operation can be finished by g_file_load_partial_contents_finish().
734    ///
735    /// Users of this function should be aware that @user_data is passed to
736    /// both the @read_more_callback and the @callback.
737    ///
738    /// If @cancellable is not [`None`], then the operation can be cancelled by
739    /// triggering the cancellable object from another thread. If the operation
740    /// was cancelled, the error [`IOErrorEnum::Cancelled`][crate::IOErrorEnum::Cancelled] will be returned.
741    /// ## `cancellable`
742    /// optional #GCancellable object, [`None`] to ignore
743    /// ## `read_more_callback`
744    /// a
745    ///   #GFileReadMoreCallback to receive partial data
746    ///   and to specify whether further data should be read
747    /// ## `callback`
748    /// a #GAsyncReadyCallback to call
749    ///   when the request is satisfied
750    #[doc(alias = "g_file_load_partial_contents_async")]
751    fn load_partial_contents_async<
752        P: FnMut(&[u8]) -> bool + 'static,
753        Q: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
754            + 'static,
755    >(
756        &self,
757        cancellable: Option<&impl IsA<Cancellable>>,
758        read_more_callback: P,
759        callback: Q,
760    ) {
761        let main_context = glib::MainContext::ref_thread_default();
762        let is_main_context_owner = main_context.is_owner();
763        let has_acquired_main_context = (!is_main_context_owner)
764            .then(|| main_context.acquire().ok())
765            .flatten();
766        assert!(
767            is_main_context_owner || has_acquired_main_context.is_some(),
768            "Async operations only allowed if the thread is owning the MainContext"
769        );
770
771        let user_data: Box<(
772            glib::thread_guard::ThreadGuard<Q>,
773            RefCell<glib::thread_guard::ThreadGuard<P>>,
774        )> = Box::new((
775            glib::thread_guard::ThreadGuard::new(callback),
776            RefCell::new(glib::thread_guard::ThreadGuard::new(read_more_callback)),
777        ));
778        unsafe extern "C" fn load_partial_contents_async_trampoline<
779            P: FnMut(&[u8]) -> bool + 'static,
780            Q: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
781                + 'static,
782        >(
783            _source_object: *mut glib::gobject_ffi::GObject,
784            res: *mut crate::ffi::GAsyncResult,
785            user_data: glib::ffi::gpointer,
786        ) {
787            let mut contents = ptr::null_mut();
788            let mut length = mem::MaybeUninit::uninit();
789            let mut etag_out = ptr::null_mut();
790            let mut error = ptr::null_mut();
791            ffi::g_file_load_partial_contents_finish(
792                _source_object as *mut _,
793                res,
794                &mut contents,
795                length.as_mut_ptr(),
796                &mut etag_out,
797                &mut error,
798            );
799            let result = if error.is_null() {
800                Ok((
801                    FromGlibContainer::from_glib_full_num(contents, length.assume_init() as _),
802                    from_glib_full(etag_out),
803                ))
804            } else {
805                Err(from_glib_full(error))
806            };
807            let callback: Box<(
808                glib::thread_guard::ThreadGuard<Q>,
809                RefCell<glib::thread_guard::ThreadGuard<P>>,
810            )> = Box::from_raw(user_data as *mut _);
811            let callback = callback.0.into_inner();
812            callback(result);
813        }
814        unsafe extern "C" fn load_partial_contents_async_read_more_trampoline<
815            P: FnMut(&[u8]) -> bool + 'static,
816            Q: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
817                + 'static,
818        >(
819            file_contents: *const libc::c_char,
820            file_size: i64,
821            user_data: glib::ffi::gpointer,
822        ) -> glib::ffi::gboolean {
823            use std::slice;
824
825            let callback: &(
826                glib::thread_guard::ThreadGuard<Q>,
827                RefCell<glib::thread_guard::ThreadGuard<P>>,
828            ) = &*(user_data as *const _);
829            let data = if file_size == 0 {
830                &[]
831            } else {
832                slice::from_raw_parts(file_contents as *const u8, file_size as usize)
833            };
834
835            (*callback.1.borrow_mut().get_mut())(data).into_glib()
836        }
837
838        let user_data = Box::into_raw(user_data) as *mut _;
839
840        unsafe {
841            ffi::g_file_load_partial_contents_async(
842                self.as_ref().to_glib_none().0,
843                cancellable.map(|p| p.as_ref()).to_glib_none().0,
844                Some(load_partial_contents_async_read_more_trampoline::<P, Q>),
845                Some(load_partial_contents_async_trampoline::<P, Q>),
846                user_data,
847            );
848        }
849    }
850
851    /// Recursively measures the disk usage of @self.
852    ///
853    /// This is essentially an analog of the 'du' command, but it also
854    /// reports the number of directories and non-directory files encountered
855    /// (including things like symbolic links).
856    ///
857    /// By default, errors are only reported against the toplevel file
858    /// itself.  Errors found while recursing are silently ignored, unless
859    /// [`FileMeasureFlags::REPORT_ANY_ERROR`][crate::FileMeasureFlags::REPORT_ANY_ERROR] is given in @flags.
860    ///
861    /// The returned size, @disk_usage, is in bytes and should be formatted
862    /// with g_format_size() in order to get something reasonable for showing
863    /// in a user interface.
864    ///
865    /// @progress_callback and @progress_data can be given to request
866    /// periodic progress updates while scanning.  See the documentation for
867    /// #GFileMeasureProgressCallback for information about when and how the
868    /// callback will be invoked.
869    /// ## `flags`
870    /// #GFileMeasureFlags
871    /// ## `cancellable`
872    /// optional #GCancellable
873    /// ## `progress_callback`
874    /// a #GFileMeasureProgressCallback
875    /// ## `progress_data`
876    /// user_data for @progress_callback
877    ///
878    /// # Returns
879    ///
880    /// [`true`] if successful, with the out parameters set.
881    ///   [`false`] otherwise, with @error set.
882    ///
883    /// ## `disk_usage`
884    /// the number of bytes of disk space used
885    ///
886    /// ## `num_dirs`
887    /// the number of directories encountered
888    ///
889    /// ## `num_files`
890    /// the number of non-directories encountered
891    #[doc(alias = "g_file_measure_disk_usage")]
892    fn measure_disk_usage(
893        &self,
894        flags: crate::FileMeasureFlags,
895        cancellable: Option<&impl IsA<Cancellable>>,
896        progress_callback: Option<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>,
897    ) -> Result<(u64, u64, u64), glib::Error> {
898        let progress_callback_data: Box<
899            Option<RefCell<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>>,
900        > = Box::new(progress_callback.map(RefCell::new));
901        unsafe extern "C" fn progress_callback_func(
902            reporting: glib::ffi::gboolean,
903            current_size: u64,
904            num_dirs: u64,
905            num_files: u64,
906            user_data: glib::ffi::gpointer,
907        ) {
908            let reporting = from_glib(reporting);
909            let callback: &Option<RefCell<Box<dyn Fn(bool, u64, u64, u64) + 'static>>> =
910                &*(user_data as *mut _);
911            if let Some(ref callback) = *callback {
912                (*callback.borrow_mut())(reporting, current_size, num_dirs, num_files)
913            } else {
914                panic!("cannot get closure...")
915            };
916        }
917        let progress_callback = if progress_callback_data.is_some() {
918            Some(progress_callback_func as _)
919        } else {
920            None
921        };
922        let super_callback0: Box<Option<RefCell<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>>> =
923            progress_callback_data;
924        unsafe {
925            let mut disk_usage = mem::MaybeUninit::uninit();
926            let mut num_dirs = mem::MaybeUninit::uninit();
927            let mut num_files = mem::MaybeUninit::uninit();
928            let mut error = ptr::null_mut();
929            let _ = ffi::g_file_measure_disk_usage(
930                self.as_ref().to_glib_none().0,
931                flags.into_glib(),
932                cancellable.map(|p| p.as_ref()).to_glib_none().0,
933                progress_callback,
934                Box::into_raw(super_callback0) as *mut _,
935                disk_usage.as_mut_ptr(),
936                num_dirs.as_mut_ptr(),
937                num_files.as_mut_ptr(),
938                &mut error,
939            );
940            let disk_usage = disk_usage.assume_init();
941            let num_dirs = num_dirs.assume_init();
942            let num_files = num_files.assume_init();
943            if error.is_null() {
944                Ok((disk_usage, num_dirs, num_files))
945            } else {
946                Err(from_glib_full(error))
947            }
948        }
949    }
950
951    /// Recursively measures the disk usage of @self.
952    ///
953    /// This is the asynchronous version of g_file_measure_disk_usage().  See
954    /// there for more information.
955    /// ## `flags`
956    /// #GFileMeasureFlags
957    /// ## `io_priority`
958    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
959    /// ## `cancellable`
960    /// optional #GCancellable
961    /// ## `progress_callback`
962    /// a #GFileMeasureProgressCallback
963    /// ## `progress_data`
964    /// user_data for @progress_callback
965    /// ## `callback`
966    /// a #GAsyncReadyCallback to call when complete
967    #[doc(alias = "g_file_measure_disk_usage_async")]
968    fn measure_disk_usage_async<P: FnOnce(Result<(u64, u64, u64), glib::Error>) + 'static>(
969        &self,
970        flags: crate::FileMeasureFlags,
971        io_priority: glib::Priority,
972        cancellable: Option<&impl IsA<Cancellable>>,
973        progress_callback: Option<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>,
974        callback: P,
975    ) {
976        let main_context = glib::MainContext::ref_thread_default();
977        let is_main_context_owner = main_context.is_owner();
978        let has_acquired_main_context = (!is_main_context_owner)
979            .then(|| main_context.acquire().ok())
980            .flatten();
981        assert!(
982            is_main_context_owner || has_acquired_main_context.is_some(),
983            "Async operations only allowed if the thread is owning the MainContext"
984        );
985
986        let progress_callback_trampoline = if progress_callback.is_some() {
987            Some(measure_disk_usage_async_progress_trampoline::<P> as _)
988        } else {
989            None
990        };
991
992        let user_data: Box<(
993            glib::thread_guard::ThreadGuard<P>,
994            RefCell<
995                Option<
996                    glib::thread_guard::ThreadGuard<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>,
997                >,
998            >,
999        )> = Box::new((
1000            glib::thread_guard::ThreadGuard::new(callback),
1001            RefCell::new(progress_callback.map(glib::thread_guard::ThreadGuard::new)),
1002        ));
1003        unsafe extern "C" fn measure_disk_usage_async_trampoline<
1004            P: FnOnce(Result<(u64, u64, u64), glib::Error>) + 'static,
1005        >(
1006            _source_object: *mut glib::gobject_ffi::GObject,
1007            res: *mut crate::ffi::GAsyncResult,
1008            user_data: glib::ffi::gpointer,
1009        ) {
1010            let mut disk_usage = mem::MaybeUninit::uninit();
1011            let mut num_dirs = mem::MaybeUninit::uninit();
1012            let mut num_files = mem::MaybeUninit::uninit();
1013            let mut error = ptr::null_mut();
1014            ffi::g_file_measure_disk_usage_finish(
1015                _source_object as *mut _,
1016                res,
1017                disk_usage.as_mut_ptr(),
1018                num_dirs.as_mut_ptr(),
1019                num_files.as_mut_ptr(),
1020                &mut error,
1021            );
1022            let result = if error.is_null() {
1023                Ok((
1024                    disk_usage.assume_init(),
1025                    num_dirs.assume_init(),
1026                    num_files.assume_init(),
1027                ))
1028            } else {
1029                Err(from_glib_full(error))
1030            };
1031            let callback: Box<(
1032                glib::thread_guard::ThreadGuard<P>,
1033                RefCell<
1034                    Option<
1035                        glib::thread_guard::ThreadGuard<
1036                            Box<dyn FnMut(bool, u64, u64, u64) + 'static>,
1037                        >,
1038                    >,
1039                >,
1040            )> = Box::from_raw(user_data as *mut _);
1041            let callback = callback.0.into_inner();
1042            callback(result);
1043        }
1044        unsafe extern "C" fn measure_disk_usage_async_progress_trampoline<
1045            P: FnOnce(Result<(u64, u64, u64), glib::Error>) + 'static,
1046        >(
1047            reporting: glib::ffi::gboolean,
1048            disk_usage: u64,
1049            num_dirs: u64,
1050            num_files: u64,
1051            user_data: glib::ffi::gpointer,
1052        ) {
1053            let callback: &(
1054                glib::thread_guard::ThreadGuard<P>,
1055                RefCell<
1056                    Option<
1057                        glib::thread_guard::ThreadGuard<
1058                            Box<dyn FnMut(bool, u64, u64, u64) + 'static>,
1059                        >,
1060                    >,
1061                >,
1062            ) = &*(user_data as *const _);
1063            (callback
1064                .1
1065                .borrow_mut()
1066                .as_mut()
1067                .expect("can't get callback")
1068                .get_mut())(from_glib(reporting), disk_usage, num_dirs, num_files);
1069        }
1070
1071        let user_data = Box::into_raw(user_data) as *mut _;
1072
1073        unsafe {
1074            ffi::g_file_measure_disk_usage_async(
1075                self.as_ref().to_glib_none().0,
1076                flags.into_glib(),
1077                io_priority.into_glib(),
1078                cancellable.map(|p| p.as_ref()).to_glib_none().0,
1079                progress_callback_trampoline,
1080                user_data,
1081                Some(measure_disk_usage_async_trampoline::<P>),
1082                user_data,
1083            );
1084        }
1085    }
1086
1087    fn measure_disk_usage_future(
1088        &self,
1089        flags: crate::FileMeasureFlags,
1090        io_priority: glib::Priority,
1091    ) -> (
1092        Pin<Box<dyn std::future::Future<Output = Result<(u64, u64, u64), glib::Error>> + 'static>>,
1093        Pin<Box<dyn futures_core::stream::Stream<Item = (bool, u64, u64, u64)> + 'static>>,
1094    ) {
1095        let (sender, receiver) = futures_channel::mpsc::unbounded();
1096
1097        let fut = Box::pin(crate::GioFuture::new(
1098            self,
1099            move |obj, cancellable, send| {
1100                obj.measure_disk_usage_async(
1101                    flags,
1102                    io_priority,
1103                    Some(cancellable),
1104                    Some(Box::new(
1105                        move |reporting, disk_usage, num_dirs, num_files| {
1106                            let _ =
1107                                sender.unbounded_send((reporting, disk_usage, num_dirs, num_files));
1108                        },
1109                    )),
1110                    move |res| {
1111                        send.resolve(res);
1112                    },
1113                );
1114            },
1115        ));
1116
1117        (fut, Box::pin(receiver))
1118    }
1119
1120    /// Asynchronously moves a file @self to the location of @destination. For details of the behaviour, see g_file_move().
1121    ///
1122    /// If @progress_callback is not [`None`], then that function that will be called
1123    /// just like in g_file_move(). The callback will run in the default main context
1124    /// of the thread calling g_file_move_async() — the same context as @callback is
1125    /// run in.
1126    ///
1127    /// When the operation is finished, @callback will be called. You can then call
1128    /// g_file_move_finish() to get the result of the operation.
1129    /// ## `destination`
1130    /// #GFile pointing to the destination location
1131    /// ## `flags`
1132    /// set of #GFileCopyFlags
1133    /// ## `io_priority`
1134    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
1135    /// ## `cancellable`
1136    /// optional #GCancellable object,
1137    ///   [`None`] to ignore
1138    /// ## `progress_callback`
1139    ///
1140    ///   #GFileProgressCallback function for updates
1141    /// ## `progress_callback_data`
1142    /// gpointer to user data for the callback function
1143    /// ## `callback`
1144    /// a #GAsyncReadyCallback
1145    ///   to call when the request is satisfied
1146    #[cfg(feature = "v2_72")]
1147    #[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
1148    #[doc(alias = "g_file_move_async")]
1149    fn move_async<Q: FnOnce(Result<(), glib::Error>) + 'static>(
1150        &self,
1151        destination: &impl IsA<File>,
1152        flags: crate::FileCopyFlags,
1153        io_priority: glib::Priority,
1154        cancellable: Option<&impl IsA<Cancellable>>,
1155        progress_callback: Option<Box<dyn FnMut(i64, i64)>>,
1156        callback: Q,
1157    ) {
1158        let main_context = glib::MainContext::ref_thread_default();
1159        let is_main_context_owner = main_context.is_owner();
1160        let has_acquired_main_context = (!is_main_context_owner)
1161            .then(|| main_context.acquire().ok())
1162            .flatten();
1163        assert!(
1164            is_main_context_owner || has_acquired_main_context.is_some(),
1165            "Async operations only allowed if the thread is owning the MainContext"
1166        );
1167
1168        let progress_trampoline = if progress_callback.is_some() {
1169            Some(move_async_progress_trampoline::<Q> as _)
1170        } else {
1171            None
1172        };
1173
1174        let user_data: Box<(
1175            glib::thread_guard::ThreadGuard<Q>,
1176            RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
1177        )> = Box::new((
1178            glib::thread_guard::ThreadGuard::new(callback),
1179            RefCell::new(progress_callback.map(glib::thread_guard::ThreadGuard::new)),
1180        ));
1181        unsafe extern "C" fn move_async_trampoline<Q: FnOnce(Result<(), glib::Error>) + 'static>(
1182            _source_object: *mut glib::gobject_ffi::GObject,
1183            res: *mut crate::ffi::GAsyncResult,
1184            user_data: glib::ffi::gpointer,
1185        ) {
1186            let mut error = ptr::null_mut();
1187            ffi::g_file_move_finish(_source_object as *mut _, res, &mut error);
1188            let result = if error.is_null() {
1189                Ok(())
1190            } else {
1191                Err(from_glib_full(error))
1192            };
1193            let callback: Box<(
1194                glib::thread_guard::ThreadGuard<Q>,
1195                RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
1196            )> = Box::from_raw(user_data as *mut _);
1197            let callback = callback.0.into_inner();
1198            callback(result);
1199        }
1200        unsafe extern "C" fn move_async_progress_trampoline<
1201            Q: FnOnce(Result<(), glib::Error>) + 'static,
1202        >(
1203            current_num_bytes: i64,
1204            total_num_bytes: i64,
1205            user_data: glib::ffi::gpointer,
1206        ) {
1207            let callback: &(
1208                glib::thread_guard::ThreadGuard<Q>,
1209                RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
1210            ) = &*(user_data as *const _);
1211            (callback
1212                .1
1213                .borrow_mut()
1214                .as_mut()
1215                .expect("no closure")
1216                .get_mut())(current_num_bytes, total_num_bytes);
1217        }
1218
1219        let user_data = Box::into_raw(user_data) as *mut _;
1220
1221        unsafe {
1222            ffi::g_file_move_async(
1223                self.as_ref().to_glib_none().0,
1224                destination.as_ref().to_glib_none().0,
1225                flags.into_glib(),
1226                io_priority.into_glib(),
1227                cancellable.map(|p| p.as_ref()).to_glib_none().0,
1228                progress_trampoline,
1229                user_data,
1230                Some(move_async_trampoline::<Q>),
1231                user_data,
1232            );
1233        }
1234    }
1235
1236    /// Asynchronously creates a symbolic link named @self which contains the
1237    /// string @symlink_value.
1238    /// ## `symlink_value`
1239    /// a string with the path for the target
1240    ///   of the new symlink
1241    /// ## `io_priority`
1242    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
1243    /// ## `cancellable`
1244    /// optional #GCancellable object,
1245    ///   [`None`] to ignore
1246    /// ## `callback`
1247    /// a #GAsyncReadyCallback to call
1248    ///   when the request is satisfied
1249    #[cfg(feature = "v2_74")]
1250    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
1251    #[doc(alias = "g_file_make_symbolic_link_async")]
1252    fn make_symbolic_link_async<P: FnOnce(Result<(), glib::Error>) + 'static>(
1253        &self,
1254        symlink_value: impl AsRef<std::path::Path>,
1255        io_priority: glib::Priority,
1256        cancellable: Option<&impl IsA<Cancellable>>,
1257        callback: P,
1258    ) {
1259        let main_context = glib::MainContext::ref_thread_default();
1260        let is_main_context_owner = main_context.is_owner();
1261        let has_acquired_main_context = (!is_main_context_owner)
1262            .then(|| main_context.acquire().ok())
1263            .flatten();
1264        assert!(
1265            is_main_context_owner || has_acquired_main_context.is_some(),
1266            "Async operations only allowed if the thread is owning the MainContext"
1267        );
1268
1269        let user_data: Box<glib::thread_guard::ThreadGuard<P>> =
1270            Box::new(glib::thread_guard::ThreadGuard::new(callback));
1271        unsafe extern "C" fn make_symbolic_link_async_trampoline<
1272            P: FnOnce(Result<(), glib::Error>) + 'static,
1273        >(
1274            _source_object: *mut glib::gobject_ffi::GObject,
1275            res: *mut crate::ffi::GAsyncResult,
1276            user_data: glib::ffi::gpointer,
1277        ) {
1278            let mut error = ptr::null_mut();
1279            let _ =
1280                ffi::g_file_make_symbolic_link_finish(_source_object as *mut _, res, &mut error);
1281            let result = if error.is_null() {
1282                Ok(())
1283            } else {
1284                Err(from_glib_full(error))
1285            };
1286            let callback: Box<glib::thread_guard::ThreadGuard<P>> =
1287                Box::from_raw(user_data as *mut _);
1288            let callback: P = callback.into_inner();
1289            callback(result);
1290        }
1291        let callback = make_symbolic_link_async_trampoline::<P>;
1292        unsafe {
1293            ffi::g_file_make_symbolic_link_async(
1294                self.as_ref().to_glib_none().0,
1295                symlink_value.as_ref().to_glib_none().0,
1296                io_priority.into_glib(),
1297                cancellable.map(|p| p.as_ref()).to_glib_none().0,
1298                Some(callback),
1299                Box::into_raw(user_data) as *mut _,
1300            );
1301        }
1302    }
1303
1304    #[cfg(feature = "v2_74")]
1305    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
1306    fn make_symbolic_link_future(
1307        &self,
1308        symlink_value: impl AsRef<std::path::Path>,
1309        io_priority: glib::Priority,
1310    ) -> Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>> {
1311        let symlink_value = symlink_value.as_ref().to_owned();
1312        Box::pin(crate::GioFuture::new(
1313            self,
1314            move |obj, cancellable, send| {
1315                obj.make_symbolic_link_async(
1316                    &symlink_value,
1317                    io_priority,
1318                    Some(cancellable),
1319                    move |res| {
1320                        send.resolve(res);
1321                    },
1322                );
1323            },
1324        ))
1325    }
1326
1327    #[cfg(feature = "v2_72")]
1328    #[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
1329    fn move_future(
1330        &self,
1331        destination: &(impl IsA<File> + Clone + 'static),
1332        flags: crate::FileCopyFlags,
1333        io_priority: glib::Priority,
1334    ) -> (
1335        Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>>,
1336        Pin<Box<dyn futures_core::stream::Stream<Item = (i64, i64)> + 'static>>,
1337    ) {
1338        let destination = destination.clone();
1339
1340        let (sender, receiver) = futures_channel::mpsc::unbounded();
1341
1342        let fut = Box::pin(crate::GioFuture::new(
1343            self,
1344            move |obj, cancellable, send| {
1345                obj.move_async(
1346                    &destination,
1347                    flags,
1348                    io_priority,
1349                    Some(cancellable),
1350                    Some(Box::new(move |current_num_bytes, total_num_bytes| {
1351                        let _ = sender.unbounded_send((current_num_bytes, total_num_bytes));
1352                    })),
1353                    move |res| {
1354                        send.resolve(res);
1355                    },
1356                );
1357            },
1358        ));
1359
1360        (fut, Box::pin(receiver))
1361    }
1362}
1363
1364impl<O: IsA<File>> FileExtManual for O {}