Skip to main content

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 the asynchronous version of g_file_measure_disk_usage().  See
863    /// there for more information.
864    /// ## `flags`
865    /// #GFileMeasureFlags
866    /// ## `io_priority`
867    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
868    /// ## `cancellable`
869    /// optional #GCancellable
870    /// ## `progress_callback`
871    /// a #GFileMeasureProgressCallback
872    /// ## `progress_data`
873    /// user_data for @progress_callback
874    /// ## `callback`
875    /// a #GAsyncReadyCallback to call when complete
876    #[doc(alias = "g_file_measure_disk_usage_async")]
877    fn measure_disk_usage_async<P: FnOnce(Result<(u64, u64, u64), glib::Error>) + 'static>(
878        &self,
879        flags: crate::FileMeasureFlags,
880        io_priority: glib::Priority,
881        cancellable: Option<&impl IsA<Cancellable>>,
882        progress_callback: Option<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>,
883        callback: P,
884    ) {
885        let main_context = glib::MainContext::ref_thread_default();
886        let is_main_context_owner = main_context.is_owner();
887        let has_acquired_main_context = (!is_main_context_owner)
888            .then(|| main_context.acquire().ok())
889            .flatten();
890        assert!(
891            is_main_context_owner || has_acquired_main_context.is_some(),
892            "Async operations only allowed if the thread is owning the MainContext"
893        );
894
895        let progress_callback_trampoline = if progress_callback.is_some() {
896            Some(measure_disk_usage_async_progress_trampoline::<P> as _)
897        } else {
898            None
899        };
900
901        let user_data: Box<(
902            glib::thread_guard::ThreadGuard<P>,
903            RefCell<
904                Option<
905                    glib::thread_guard::ThreadGuard<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>,
906                >,
907            >,
908        )> = Box::new((
909            glib::thread_guard::ThreadGuard::new(callback),
910            RefCell::new(progress_callback.map(glib::thread_guard::ThreadGuard::new)),
911        ));
912        unsafe extern "C" fn measure_disk_usage_async_trampoline<
913            P: FnOnce(Result<(u64, u64, u64), glib::Error>) + 'static,
914        >(
915            _source_object: *mut glib::gobject_ffi::GObject,
916            res: *mut crate::ffi::GAsyncResult,
917            user_data: glib::ffi::gpointer,
918        ) {
919            unsafe {
920                let mut disk_usage = mem::MaybeUninit::uninit();
921                let mut num_dirs = mem::MaybeUninit::uninit();
922                let mut num_files = mem::MaybeUninit::uninit();
923                let mut error = ptr::null_mut();
924                ffi::g_file_measure_disk_usage_finish(
925                    _source_object as *mut _,
926                    res,
927                    disk_usage.as_mut_ptr(),
928                    num_dirs.as_mut_ptr(),
929                    num_files.as_mut_ptr(),
930                    &mut error,
931                );
932                let result = if error.is_null() {
933                    Ok((
934                        disk_usage.assume_init(),
935                        num_dirs.assume_init(),
936                        num_files.assume_init(),
937                    ))
938                } else {
939                    Err(from_glib_full(error))
940                };
941                let callback: Box<(
942                    glib::thread_guard::ThreadGuard<P>,
943                    RefCell<
944                        Option<
945                            glib::thread_guard::ThreadGuard<
946                                Box<dyn FnMut(bool, u64, u64, u64) + 'static>,
947                            >,
948                        >,
949                    >,
950                )> = Box::from_raw(user_data as *mut _);
951                let callback = callback.0.into_inner();
952                callback(result);
953            }
954        }
955        unsafe extern "C" fn measure_disk_usage_async_progress_trampoline<
956            P: FnOnce(Result<(u64, u64, u64), glib::Error>) + 'static,
957        >(
958            reporting: glib::ffi::gboolean,
959            disk_usage: u64,
960            num_dirs: u64,
961            num_files: u64,
962            user_data: glib::ffi::gpointer,
963        ) {
964            unsafe {
965                let callback: &(
966                    glib::thread_guard::ThreadGuard<P>,
967                    RefCell<
968                        Option<
969                            glib::thread_guard::ThreadGuard<
970                                Box<dyn FnMut(bool, u64, u64, u64) + 'static>,
971                            >,
972                        >,
973                    >,
974                ) = &*(user_data as *const _);
975                (callback
976                    .1
977                    .borrow_mut()
978                    .as_mut()
979                    .expect("can't get callback")
980                    .get_mut())(
981                    from_glib(reporting), disk_usage, num_dirs, num_files
982                );
983            }
984        }
985
986        let user_data = Box::into_raw(user_data) as *mut _;
987
988        unsafe {
989            ffi::g_file_measure_disk_usage_async(
990                self.as_ref().to_glib_none().0,
991                flags.into_glib(),
992                io_priority.into_glib(),
993                cancellable.map(|p| p.as_ref()).to_glib_none().0,
994                progress_callback_trampoline,
995                user_data,
996                Some(measure_disk_usage_async_trampoline::<P>),
997                user_data,
998            );
999        }
1000    }
1001
1002    fn measure_disk_usage_future(
1003        &self,
1004        flags: crate::FileMeasureFlags,
1005        io_priority: glib::Priority,
1006    ) -> (
1007        Pin<Box<dyn std::future::Future<Output = Result<(u64, u64, u64), glib::Error>> + 'static>>,
1008        Pin<Box<dyn futures_core::stream::Stream<Item = (bool, u64, u64, u64)> + 'static>>,
1009    ) {
1010        let (sender, receiver) = futures_channel::mpsc::unbounded();
1011
1012        let fut = Box::pin(crate::GioFuture::new(
1013            self,
1014            move |obj, cancellable, send| {
1015                obj.measure_disk_usage_async(
1016                    flags,
1017                    io_priority,
1018                    Some(cancellable),
1019                    Some(Box::new(
1020                        move |reporting, disk_usage, num_dirs, num_files| {
1021                            let _ =
1022                                sender.unbounded_send((reporting, disk_usage, num_dirs, num_files));
1023                        },
1024                    )),
1025                    move |res| {
1026                        send.resolve(res);
1027                    },
1028                );
1029            },
1030        ));
1031
1032        (fut, Box::pin(receiver))
1033    }
1034
1035    /// Asynchronously moves a file @self to the location of @destination. For details of the behaviour, see g_file_move().
1036    ///
1037    /// If @progress_callback is not [`None`], then that function that will be called
1038    /// just like in g_file_move(). The callback will run in the default main context
1039    /// of the thread calling g_file_move_async() — the same context as @callback is
1040    /// run in.
1041    ///
1042    /// When the operation is finished, @callback will be called. You can then call
1043    /// g_file_move_finish() to get the result of the operation.
1044    /// ## `destination`
1045    /// #GFile pointing to the destination location
1046    /// ## `flags`
1047    /// set of #GFileCopyFlags
1048    /// ## `io_priority`
1049    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
1050    /// ## `cancellable`
1051    /// optional #GCancellable object,
1052    ///   [`None`] to ignore
1053    /// ## `progress_callback`
1054    ///
1055    ///   #GFileProgressCallback function for updates
1056    /// ## `progress_callback_data`
1057    /// gpointer to user data for the callback function
1058    /// ## `callback`
1059    /// a #GAsyncReadyCallback
1060    ///   to call when the request is satisfied
1061    #[cfg(feature = "v2_72")]
1062    #[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
1063    #[doc(alias = "g_file_move_async")]
1064    fn move_async<Q: FnOnce(Result<(), glib::Error>) + 'static>(
1065        &self,
1066        destination: &impl IsA<File>,
1067        flags: crate::FileCopyFlags,
1068        io_priority: glib::Priority,
1069        cancellable: Option<&impl IsA<Cancellable>>,
1070        progress_callback: Option<Box<dyn FnMut(i64, i64)>>,
1071        callback: Q,
1072    ) {
1073        let main_context = glib::MainContext::ref_thread_default();
1074        let is_main_context_owner = main_context.is_owner();
1075        let has_acquired_main_context = (!is_main_context_owner)
1076            .then(|| main_context.acquire().ok())
1077            .flatten();
1078        assert!(
1079            is_main_context_owner || has_acquired_main_context.is_some(),
1080            "Async operations only allowed if the thread is owning the MainContext"
1081        );
1082
1083        let progress_trampoline = if progress_callback.is_some() {
1084            Some(move_async_progress_trampoline::<Q> as _)
1085        } else {
1086            None
1087        };
1088
1089        let user_data: Box<(
1090            glib::thread_guard::ThreadGuard<Q>,
1091            RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
1092        )> = Box::new((
1093            glib::thread_guard::ThreadGuard::new(callback),
1094            RefCell::new(progress_callback.map(glib::thread_guard::ThreadGuard::new)),
1095        ));
1096        unsafe extern "C" fn move_async_trampoline<Q: FnOnce(Result<(), glib::Error>) + 'static>(
1097            _source_object: *mut glib::gobject_ffi::GObject,
1098            res: *mut crate::ffi::GAsyncResult,
1099            user_data: glib::ffi::gpointer,
1100        ) {
1101            unsafe {
1102                let mut error = ptr::null_mut();
1103                ffi::g_file_move_finish(_source_object as *mut _, res, &mut error);
1104                let result = if error.is_null() {
1105                    Ok(())
1106                } else {
1107                    Err(from_glib_full(error))
1108                };
1109                let callback: Box<(
1110                    glib::thread_guard::ThreadGuard<Q>,
1111                    RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
1112                )> = Box::from_raw(user_data as *mut _);
1113                let callback = callback.0.into_inner();
1114                callback(result);
1115            }
1116        }
1117        unsafe extern "C" fn move_async_progress_trampoline<
1118            Q: FnOnce(Result<(), glib::Error>) + 'static,
1119        >(
1120            current_num_bytes: i64,
1121            total_num_bytes: i64,
1122            user_data: glib::ffi::gpointer,
1123        ) {
1124            unsafe {
1125                let callback: &(
1126                    glib::thread_guard::ThreadGuard<Q>,
1127                    RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
1128                ) = &*(user_data as *const _);
1129                (callback
1130                    .1
1131                    .borrow_mut()
1132                    .as_mut()
1133                    .expect("no closure")
1134                    .get_mut())(current_num_bytes, total_num_bytes);
1135            }
1136        }
1137
1138        let user_data = Box::into_raw(user_data) as *mut _;
1139
1140        unsafe {
1141            ffi::g_file_move_async(
1142                self.as_ref().to_glib_none().0,
1143                destination.as_ref().to_glib_none().0,
1144                flags.into_glib(),
1145                io_priority.into_glib(),
1146                cancellable.map(|p| p.as_ref()).to_glib_none().0,
1147                progress_trampoline,
1148                user_data,
1149                Some(move_async_trampoline::<Q>),
1150                user_data,
1151            );
1152        }
1153    }
1154
1155    /// Asynchronously creates a symbolic link named @self which contains the
1156    /// string @symlink_value.
1157    /// ## `symlink_value`
1158    /// a string with the path for the target
1159    ///   of the new symlink
1160    /// ## `io_priority`
1161    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
1162    /// ## `cancellable`
1163    /// optional #GCancellable object,
1164    ///   [`None`] to ignore
1165    /// ## `callback`
1166    /// a #GAsyncReadyCallback to call
1167    ///   when the request is satisfied
1168    #[cfg(feature = "v2_74")]
1169    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
1170    #[doc(alias = "g_file_make_symbolic_link_async")]
1171    fn make_symbolic_link_async<P: FnOnce(Result<(), glib::Error>) + 'static>(
1172        &self,
1173        symlink_value: impl AsRef<std::path::Path>,
1174        io_priority: glib::Priority,
1175        cancellable: Option<&impl IsA<Cancellable>>,
1176        callback: P,
1177    ) {
1178        let main_context = glib::MainContext::ref_thread_default();
1179        let is_main_context_owner = main_context.is_owner();
1180        let has_acquired_main_context = (!is_main_context_owner)
1181            .then(|| main_context.acquire().ok())
1182            .flatten();
1183        assert!(
1184            is_main_context_owner || has_acquired_main_context.is_some(),
1185            "Async operations only allowed if the thread is owning the MainContext"
1186        );
1187
1188        let user_data: Box<glib::thread_guard::ThreadGuard<P>> =
1189            Box::new(glib::thread_guard::ThreadGuard::new(callback));
1190        unsafe extern "C" fn make_symbolic_link_async_trampoline<
1191            P: FnOnce(Result<(), glib::Error>) + 'static,
1192        >(
1193            _source_object: *mut glib::gobject_ffi::GObject,
1194            res: *mut crate::ffi::GAsyncResult,
1195            user_data: glib::ffi::gpointer,
1196        ) {
1197            unsafe {
1198                let mut error = ptr::null_mut();
1199                let _ = ffi::g_file_make_symbolic_link_finish(
1200                    _source_object as *mut _,
1201                    res,
1202                    &mut error,
1203                );
1204                let result = if error.is_null() {
1205                    Ok(())
1206                } else {
1207                    Err(from_glib_full(error))
1208                };
1209                let callback: Box<glib::thread_guard::ThreadGuard<P>> =
1210                    Box::from_raw(user_data as *mut _);
1211                let callback: P = callback.into_inner();
1212                callback(result);
1213            }
1214        }
1215        let callback = make_symbolic_link_async_trampoline::<P>;
1216        unsafe {
1217            ffi::g_file_make_symbolic_link_async(
1218                self.as_ref().to_glib_none().0,
1219                symlink_value.as_ref().to_glib_none().0,
1220                io_priority.into_glib(),
1221                cancellable.map(|p| p.as_ref()).to_glib_none().0,
1222                Some(callback),
1223                Box::into_raw(user_data) as *mut _,
1224            );
1225        }
1226    }
1227
1228    #[cfg(feature = "v2_74")]
1229    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
1230    fn make_symbolic_link_future(
1231        &self,
1232        symlink_value: impl AsRef<std::path::Path>,
1233        io_priority: glib::Priority,
1234    ) -> Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>> {
1235        let symlink_value = symlink_value.as_ref().to_owned();
1236        Box::pin(crate::GioFuture::new(
1237            self,
1238            move |obj, cancellable, send| {
1239                obj.make_symbolic_link_async(
1240                    &symlink_value,
1241                    io_priority,
1242                    Some(cancellable),
1243                    move |res| {
1244                        send.resolve(res);
1245                    },
1246                );
1247            },
1248        ))
1249    }
1250
1251    #[cfg(feature = "v2_72")]
1252    #[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
1253    fn move_future(
1254        &self,
1255        destination: &(impl IsA<File> + Clone + 'static),
1256        flags: crate::FileCopyFlags,
1257        io_priority: glib::Priority,
1258    ) -> (
1259        Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>>,
1260        Pin<Box<dyn futures_core::stream::Stream<Item = (i64, i64)> + 'static>>,
1261    ) {
1262        let destination = destination.clone();
1263
1264        let (sender, receiver) = futures_channel::mpsc::unbounded();
1265
1266        let fut = Box::pin(crate::GioFuture::new(
1267            self,
1268            move |obj, cancellable, send| {
1269                obj.move_async(
1270                    &destination,
1271                    flags,
1272                    io_priority,
1273                    Some(cancellable),
1274                    Some(Box::new(move |current_num_bytes, total_num_bytes| {
1275                        let _ = sender.unbounded_send((current_num_bytes, total_num_bytes));
1276                    })),
1277                    move |res| {
1278                        send.resolve(res);
1279                    },
1280                );
1281            },
1282        ));
1283
1284        (fut, Box::pin(receiver))
1285    }
1286
1287    #[doc(alias = "g_file_set_attribute")]
1288    fn set_attribute<'a>(
1289        &self,
1290        attribute: &str,
1291        value: impl Into<FileAttributeValue<'a>>,
1292        flags: FileQueryInfoFlags,
1293        cancellable: Option<&impl IsA<Cancellable>>,
1294    ) -> Result<(), glib::Error> {
1295        unsafe {
1296            let mut error = std::ptr::null_mut();
1297            let value: FileAttributeValue<'a> = value.into();
1298            let is_ok = ffi::g_file_set_attribute(
1299                self.as_ref().to_glib_none().0,
1300                attribute.to_glib_none().0,
1301                value.type_().into_glib(),
1302                value.as_ptr(),
1303                flags.into_glib(),
1304                cancellable.map(|p| p.as_ref()).to_glib_none().0,
1305                &mut error,
1306            );
1307            debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null());
1308            if error.is_null() {
1309                Ok(())
1310            } else {
1311                Err(from_glib_full(error))
1312            }
1313        }
1314    }
1315}
1316
1317impl<O: IsA<File>> FileExtManual for O {}