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