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