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