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    ///  the same context as @callback is
433    /// run in.
434    ///
435    /// When the operation is finished, @callback will be called. You can then call
436    /// g_file_copy_finish() to get the result of the operation.
437    /// ## `destination`
438    /// destination #GFile
439    /// ## `flags`
440    /// set of #GFileCopyFlags
441    /// ## `io_priority`
442    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
443    /// ## `cancellable`
444    /// optional #GCancellable object,
445    ///   [`None`] to ignore
446    /// ## `progress_callback`
447    ///
448    ///   function to callback with progress information, or [`None`] if
449    ///   progress information is not needed
450    /// ## `progress_callback_data`
451    /// user data to pass to @progress_callback
452    /// ## `callback`
453    /// a #GAsyncReadyCallback
454    ///   to call when the request is satisfied
455    #[doc(alias = "g_file_copy_async")]
456    fn copy_async<Q: FnOnce(Result<(), glib::Error>) + 'static>(
457        &self,
458        destination: &impl IsA<File>,
459        flags: crate::FileCopyFlags,
460        io_priority: glib::Priority,
461        cancellable: Option<&impl IsA<Cancellable>>,
462        progress_callback: Option<Box<dyn FnMut(i64, i64)>>,
463        callback: Q,
464    ) {
465        let main_context = glib::MainContext::ref_thread_default();
466        let is_main_context_owner = main_context.is_owner();
467        let has_acquired_main_context = (!is_main_context_owner)
468            .then(|| main_context.acquire().ok())
469            .flatten();
470        assert!(
471            is_main_context_owner || has_acquired_main_context.is_some(),
472            "Async operations only allowed if the thread is owning the MainContext"
473        );
474
475        let progress_trampoline = if progress_callback.is_some() {
476            Some(copy_async_progress_trampoline::<Q> as _)
477        } else {
478            None
479        };
480
481        let user_data: Box<(
482            glib::thread_guard::ThreadGuard<Q>,
483            RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
484        )> = Box::new((
485            glib::thread_guard::ThreadGuard::new(callback),
486            RefCell::new(progress_callback.map(glib::thread_guard::ThreadGuard::new)),
487        ));
488        unsafe extern "C" fn copy_async_trampoline<Q: FnOnce(Result<(), glib::Error>) + 'static>(
489            _source_object: *mut glib::gobject_ffi::GObject,
490            res: *mut crate::ffi::GAsyncResult,
491            user_data: glib::ffi::gpointer,
492        ) {
493            unsafe {
494                let mut error = ptr::null_mut();
495                ffi::g_file_copy_finish(_source_object as *mut _, res, &mut error);
496                let result = if error.is_null() {
497                    Ok(())
498                } else {
499                    Err(from_glib_full(error))
500                };
501                let callback: Box<(
502                    glib::thread_guard::ThreadGuard<Q>,
503                    RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
504                )> = Box::from_raw(user_data as *mut _);
505                let callback = callback.0.into_inner();
506                callback(result);
507            }
508        }
509        unsafe extern "C" fn copy_async_progress_trampoline<
510            Q: FnOnce(Result<(), glib::Error>) + 'static,
511        >(
512            current_num_bytes: i64,
513            total_num_bytes: i64,
514            user_data: glib::ffi::gpointer,
515        ) {
516            unsafe {
517                let callback: &(
518                    glib::thread_guard::ThreadGuard<Q>,
519                    RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
520                ) = &*(user_data as *const _);
521                (callback
522                    .1
523                    .borrow_mut()
524                    .as_mut()
525                    .expect("no closure")
526                    .get_mut())(current_num_bytes, total_num_bytes);
527            }
528        }
529
530        let user_data = Box::into_raw(user_data) as *mut _;
531
532        unsafe {
533            ffi::g_file_copy_async(
534                self.as_ref().to_glib_none().0,
535                destination.as_ref().to_glib_none().0,
536                flags.into_glib(),
537                io_priority.into_glib(),
538                cancellable.map(|p| p.as_ref()).to_glib_none().0,
539                progress_trampoline,
540                user_data,
541                Some(copy_async_trampoline::<Q>),
542                user_data,
543            );
544        }
545    }
546
547    fn copy_future(
548        &self,
549        destination: &(impl IsA<File> + Clone + 'static),
550        flags: crate::FileCopyFlags,
551        io_priority: glib::Priority,
552    ) -> (
553        Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>>,
554        Pin<Box<dyn futures_core::stream::Stream<Item = (i64, i64)> + 'static>>,
555    ) {
556        let destination = destination.clone();
557
558        let (sender, receiver) = futures_channel::mpsc::unbounded();
559
560        let fut = Box::pin(crate::GioFuture::new(
561            self,
562            move |obj, cancellable, send| {
563                obj.copy_async(
564                    &destination,
565                    flags,
566                    io_priority,
567                    Some(cancellable),
568                    Some(Box::new(move |current_num_bytes, total_num_bytes| {
569                        let _ = sender.unbounded_send((current_num_bytes, total_num_bytes));
570                    })),
571                    move |res| {
572                        send.resolve(res);
573                    },
574                );
575            },
576        ));
577
578        (fut, Box::pin(receiver))
579    }
580
581    /// Loads the content of the file into memory. The data is always
582    /// zero-terminated, but this is not included in the resultant @length.
583    /// The returned @contents should be freed with g_free() when no longer
584    /// needed.
585    ///
586    /// If @cancellable is not [`None`], then the operation can be cancelled by
587    /// triggering the cancellable object from another thread. If the operation
588    /// was cancelled, the error [`IOErrorEnum::Cancelled`][crate::IOErrorEnum::Cancelled] will be returned.
589    /// ## `cancellable`
590    /// optional #GCancellable object, [`None`] to ignore
591    ///
592    /// # Returns
593    ///
594    /// [`true`] if the @self's contents were successfully loaded.
595    ///   [`false`] if there were errors.
596    ///
597    /// ## `contents`
598    /// a location to place the contents of the file
599    ///
600    /// ## `etag_out`
601    /// a location to place the current entity tag for the file,
602    ///   or [`None`] if the entity tag is not needed
603    #[doc(alias = "g_file_load_contents")]
604    fn load_contents(
605        &self,
606        cancellable: Option<&impl IsA<Cancellable>>,
607    ) -> Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error> {
608        unsafe {
609            let mut contents = std::ptr::null_mut();
610            let mut length = std::mem::MaybeUninit::uninit();
611            let mut etag_out = std::ptr::null_mut();
612            let mut error = std::ptr::null_mut();
613            let is_ok = ffi::g_file_load_contents(
614                self.as_ref().to_glib_none().0,
615                cancellable.map(|p| p.as_ref()).to_glib_none().0,
616                &mut contents,
617                length.as_mut_ptr(),
618                &mut etag_out,
619                &mut error,
620            );
621            debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null());
622            if error.is_null() {
623                Ok((
624                    FromGlibContainer::from_glib_full_num(contents, length.assume_init() as _),
625                    from_glib_full(etag_out),
626                ))
627            } else {
628                Err(from_glib_full(error))
629            }
630        }
631    }
632
633    /// Starts an asynchronous load of the @self's contents.
634    ///
635    /// For more details, see g_file_load_contents() which is
636    /// the synchronous version of this call.
637    ///
638    /// When the load operation has completed, @callback will be called
639    /// with @user data. To finish the operation, call
640    /// g_file_load_contents_finish() with the #GAsyncResult returned by
641    /// the @callback.
642    ///
643    /// If @cancellable is not [`None`], then the operation can be cancelled by
644    /// triggering the cancellable object from another thread. If the operation
645    /// was cancelled, the error [`IOErrorEnum::Cancelled`][crate::IOErrorEnum::Cancelled] will be returned.
646    /// ## `cancellable`
647    /// optional #GCancellable object, [`None`] to ignore
648    /// ## `callback`
649    /// a #GAsyncReadyCallback to call when the request is satisfied
650    #[doc(alias = "g_file_load_contents_async")]
651    fn load_contents_async<
652        P: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
653            + 'static,
654    >(
655        &self,
656        cancellable: Option<&impl IsA<Cancellable>>,
657        callback: P,
658    ) {
659        let main_context = glib::MainContext::ref_thread_default();
660        let is_main_context_owner = main_context.is_owner();
661        let has_acquired_main_context = (!is_main_context_owner)
662            .then(|| main_context.acquire().ok())
663            .flatten();
664        assert!(
665            is_main_context_owner || has_acquired_main_context.is_some(),
666            "Async operations only allowed if the thread is owning the MainContext"
667        );
668
669        let user_data: Box<glib::thread_guard::ThreadGuard<P>> =
670            Box::new(glib::thread_guard::ThreadGuard::new(callback));
671        unsafe extern "C" fn load_contents_async_trampoline<
672            P: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
673                + 'static,
674        >(
675            _source_object: *mut glib::gobject_ffi::GObject,
676            res: *mut crate::ffi::GAsyncResult,
677            user_data: glib::ffi::gpointer,
678        ) {
679            unsafe {
680                let mut error = std::ptr::null_mut();
681                let mut contents = std::ptr::null_mut();
682                let mut length = std::mem::MaybeUninit::uninit();
683                let mut etag_out = std::ptr::null_mut();
684                let _ = ffi::g_file_load_contents_finish(
685                    _source_object as *mut _,
686                    res,
687                    &mut contents,
688                    length.as_mut_ptr(),
689                    &mut etag_out,
690                    &mut error,
691                );
692                let result = if error.is_null() {
693                    Ok((
694                        FromGlibContainer::from_glib_full_num(contents, length.assume_init() as _),
695                        from_glib_full(etag_out),
696                    ))
697                } else {
698                    Err(from_glib_full(error))
699                };
700                let callback: Box<glib::thread_guard::ThreadGuard<P>> =
701                    Box::from_raw(user_data as *mut _);
702                let callback: P = callback.into_inner();
703                callback(result);
704            }
705        }
706        let callback = load_contents_async_trampoline::<P>;
707        unsafe {
708            ffi::g_file_load_contents_async(
709                self.as_ref().to_glib_none().0,
710                cancellable.map(|p| p.as_ref()).to_glib_none().0,
711                Some(callback),
712                Box::into_raw(user_data) as *mut _,
713            );
714        }
715    }
716
717    fn load_contents_future(
718        &self,
719    ) -> impl std::future::Future<
720        Output = Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>,
721    > + Unpin
722    + 'static {
723        crate::GioFuture::new(self, move |obj, cancellable, send| {
724            obj.load_contents_async(Some(cancellable), move |res| {
725                send.resolve(res);
726            });
727        })
728    }
729
730    /// Reads the partial contents of a file. A #GFileReadMoreCallback should
731    /// be used to stop reading from the file when appropriate, else this
732    /// function will behave exactly as g_file_load_contents_async(). This
733    /// operation can be finished by g_file_load_partial_contents_finish().
734    ///
735    /// Users of this function should be aware that @user_data is passed to
736    /// both the @read_more_callback and the @callback.
737    ///
738    /// If @cancellable is not [`None`], then the operation can be cancelled by
739    /// triggering the cancellable object from another thread. If the operation
740    /// was cancelled, the error [`IOErrorEnum::Cancelled`][crate::IOErrorEnum::Cancelled] will be returned.
741    /// ## `cancellable`
742    /// optional #GCancellable object, [`None`] to ignore
743    /// ## `read_more_callback`
744    /// a
745    ///   #GFileReadMoreCallback to receive partial data
746    ///   and to specify whether further data should be read
747    /// ## `callback`
748    /// a #GAsyncReadyCallback to call
749    ///   when the request is satisfied
750    #[doc(alias = "g_file_load_partial_contents_async")]
751    fn load_partial_contents_async<
752        P: FnMut(&[u8]) -> bool + 'static,
753        Q: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
754            + 'static,
755    >(
756        &self,
757        cancellable: Option<&impl IsA<Cancellable>>,
758        read_more_callback: P,
759        callback: Q,
760    ) {
761        let main_context = glib::MainContext::ref_thread_default();
762        let is_main_context_owner = main_context.is_owner();
763        let has_acquired_main_context = (!is_main_context_owner)
764            .then(|| main_context.acquire().ok())
765            .flatten();
766        assert!(
767            is_main_context_owner || has_acquired_main_context.is_some(),
768            "Async operations only allowed if the thread is owning the MainContext"
769        );
770
771        let user_data: Box<(
772            glib::thread_guard::ThreadGuard<Q>,
773            RefCell<glib::thread_guard::ThreadGuard<P>>,
774        )> = Box::new((
775            glib::thread_guard::ThreadGuard::new(callback),
776            RefCell::new(glib::thread_guard::ThreadGuard::new(read_more_callback)),
777        ));
778        unsafe extern "C" fn load_partial_contents_async_trampoline<
779            P: FnMut(&[u8]) -> bool + 'static,
780            Q: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
781                + 'static,
782        >(
783            _source_object: *mut glib::gobject_ffi::GObject,
784            res: *mut crate::ffi::GAsyncResult,
785            user_data: glib::ffi::gpointer,
786        ) {
787            unsafe {
788                let mut contents = ptr::null_mut();
789                let mut length = mem::MaybeUninit::uninit();
790                let mut etag_out = ptr::null_mut();
791                let mut error = ptr::null_mut();
792                ffi::g_file_load_partial_contents_finish(
793                    _source_object as *mut _,
794                    res,
795                    &mut contents,
796                    length.as_mut_ptr(),
797                    &mut etag_out,
798                    &mut error,
799                );
800                let result = if error.is_null() {
801                    Ok((
802                        FromGlibContainer::from_glib_full_num(contents, length.assume_init() as _),
803                        from_glib_full(etag_out),
804                    ))
805                } else {
806                    Err(from_glib_full(error))
807                };
808                let callback: Box<(
809                    glib::thread_guard::ThreadGuard<Q>,
810                    RefCell<glib::thread_guard::ThreadGuard<P>>,
811                )> = Box::from_raw(user_data as *mut _);
812                let callback = callback.0.into_inner();
813                callback(result);
814            }
815        }
816        unsafe extern "C" fn load_partial_contents_async_read_more_trampoline<
817            P: FnMut(&[u8]) -> bool + 'static,
818            Q: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
819                + 'static,
820        >(
821            file_contents: *const libc::c_char,
822            file_size: i64,
823            user_data: glib::ffi::gpointer,
824        ) -> glib::ffi::gboolean {
825            unsafe {
826                use std::slice;
827
828                let callback: &(
829                    glib::thread_guard::ThreadGuard<Q>,
830                    RefCell<glib::thread_guard::ThreadGuard<P>>,
831                ) = &*(user_data as *const _);
832                let data = if file_size == 0 {
833                    &[]
834                } else {
835                    slice::from_raw_parts(file_contents as *const u8, file_size as usize)
836                };
837
838                (*callback.1.borrow_mut().get_mut())(data).into_glib()
839            }
840        }
841
842        let user_data = Box::into_raw(user_data) as *mut _;
843
844        unsafe {
845            ffi::g_file_load_partial_contents_async(
846                self.as_ref().to_glib_none().0,
847                cancellable.map(|p| p.as_ref()).to_glib_none().0,
848                Some(load_partial_contents_async_read_more_trampoline::<P, Q>),
849                Some(load_partial_contents_async_trampoline::<P, Q>),
850                user_data,
851            );
852        }
853    }
854
855    /// Recursively measures the disk usage of @self.
856    ///
857    /// This is the asynchronous version of g_file_measure_disk_usage().  See
858    /// there for more information.
859    /// ## `flags`
860    /// #GFileMeasureFlags
861    /// ## `io_priority`
862    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
863    /// ## `cancellable`
864    /// optional #GCancellable
865    /// ## `progress_callback`
866    /// a #GFileMeasureProgressCallback
867    /// ## `progress_data`
868    /// user_data for @progress_callback
869    /// ## `callback`
870    /// a #GAsyncReadyCallback to call when complete
871    #[doc(alias = "g_file_measure_disk_usage_async")]
872    fn measure_disk_usage_async<P: FnOnce(Result<(u64, u64, u64), glib::Error>) + 'static>(
873        &self,
874        flags: crate::FileMeasureFlags,
875        io_priority: glib::Priority,
876        cancellable: Option<&impl IsA<Cancellable>>,
877        progress_callback: Option<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>,
878        callback: P,
879    ) {
880        let main_context = glib::MainContext::ref_thread_default();
881        let is_main_context_owner = main_context.is_owner();
882        let has_acquired_main_context = (!is_main_context_owner)
883            .then(|| main_context.acquire().ok())
884            .flatten();
885        assert!(
886            is_main_context_owner || has_acquired_main_context.is_some(),
887            "Async operations only allowed if the thread is owning the MainContext"
888        );
889
890        let progress_callback_trampoline = if progress_callback.is_some() {
891            Some(measure_disk_usage_async_progress_trampoline::<P> as _)
892        } else {
893            None
894        };
895
896        let user_data: Box<(
897            glib::thread_guard::ThreadGuard<P>,
898            RefCell<
899                Option<
900                    glib::thread_guard::ThreadGuard<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>,
901                >,
902            >,
903        )> = Box::new((
904            glib::thread_guard::ThreadGuard::new(callback),
905            RefCell::new(progress_callback.map(glib::thread_guard::ThreadGuard::new)),
906        ));
907        unsafe extern "C" fn measure_disk_usage_async_trampoline<
908            P: FnOnce(Result<(u64, u64, u64), glib::Error>) + 'static,
909        >(
910            _source_object: *mut glib::gobject_ffi::GObject,
911            res: *mut crate::ffi::GAsyncResult,
912            user_data: glib::ffi::gpointer,
913        ) {
914            unsafe {
915                let mut disk_usage = mem::MaybeUninit::uninit();
916                let mut num_dirs = mem::MaybeUninit::uninit();
917                let mut num_files = mem::MaybeUninit::uninit();
918                let mut error = ptr::null_mut();
919                ffi::g_file_measure_disk_usage_finish(
920                    _source_object as *mut _,
921                    res,
922                    disk_usage.as_mut_ptr(),
923                    num_dirs.as_mut_ptr(),
924                    num_files.as_mut_ptr(),
925                    &mut error,
926                );
927                let result = if error.is_null() {
928                    Ok((
929                        disk_usage.assume_init(),
930                        num_dirs.assume_init(),
931                        num_files.assume_init(),
932                    ))
933                } else {
934                    Err(from_glib_full(error))
935                };
936                let callback: Box<(
937                    glib::thread_guard::ThreadGuard<P>,
938                    RefCell<
939                        Option<
940                            glib::thread_guard::ThreadGuard<
941                                Box<dyn FnMut(bool, u64, u64, u64) + 'static>,
942                            >,
943                        >,
944                    >,
945                )> = Box::from_raw(user_data as *mut _);
946                let callback = callback.0.into_inner();
947                callback(result);
948            }
949        }
950        unsafe extern "C" fn measure_disk_usage_async_progress_trampoline<
951            P: FnOnce(Result<(u64, u64, u64), glib::Error>) + 'static,
952        >(
953            reporting: glib::ffi::gboolean,
954            disk_usage: u64,
955            num_dirs: u64,
956            num_files: u64,
957            user_data: glib::ffi::gpointer,
958        ) {
959            unsafe {
960                let callback: &(
961                    glib::thread_guard::ThreadGuard<P>,
962                    RefCell<
963                        Option<
964                            glib::thread_guard::ThreadGuard<
965                                Box<dyn FnMut(bool, u64, u64, u64) + 'static>,
966                            >,
967                        >,
968                    >,
969                ) = &*(user_data as *const _);
970                (callback
971                    .1
972                    .borrow_mut()
973                    .as_mut()
974                    .expect("can't get callback")
975                    .get_mut())(
976                    from_glib(reporting), disk_usage, num_dirs, num_files
977                );
978            }
979        }
980
981        let user_data = Box::into_raw(user_data) as *mut _;
982
983        unsafe {
984            ffi::g_file_measure_disk_usage_async(
985                self.as_ref().to_glib_none().0,
986                flags.into_glib(),
987                io_priority.into_glib(),
988                cancellable.map(|p| p.as_ref()).to_glib_none().0,
989                progress_callback_trampoline,
990                user_data,
991                Some(measure_disk_usage_async_trampoline::<P>),
992                user_data,
993            );
994        }
995    }
996
997    fn measure_disk_usage_future(
998        &self,
999        flags: crate::FileMeasureFlags,
1000        io_priority: glib::Priority,
1001    ) -> (
1002        Pin<Box<dyn std::future::Future<Output = Result<(u64, u64, u64), glib::Error>> + 'static>>,
1003        Pin<Box<dyn futures_core::stream::Stream<Item = (bool, u64, u64, u64)> + 'static>>,
1004    ) {
1005        let (sender, receiver) = futures_channel::mpsc::unbounded();
1006
1007        let fut = Box::pin(crate::GioFuture::new(
1008            self,
1009            move |obj, cancellable, send| {
1010                obj.measure_disk_usage_async(
1011                    flags,
1012                    io_priority,
1013                    Some(cancellable),
1014                    Some(Box::new(
1015                        move |reporting, disk_usage, num_dirs, num_files| {
1016                            let _ =
1017                                sender.unbounded_send((reporting, disk_usage, num_dirs, num_files));
1018                        },
1019                    )),
1020                    move |res| {
1021                        send.resolve(res);
1022                    },
1023                );
1024            },
1025        ));
1026
1027        (fut, Box::pin(receiver))
1028    }
1029
1030    ///  the same context as @callback is
1031    /// run in.
1032    ///
1033    /// When the operation is finished, @callback will be called. You can then call
1034    /// g_file_move_finish() to get the result of the operation.
1035    /// ## `destination`
1036    /// #GFile pointing to the destination location
1037    /// ## `flags`
1038    /// set of #GFileCopyFlags
1039    /// ## `io_priority`
1040    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
1041    /// ## `cancellable`
1042    /// optional #GCancellable object,
1043    ///   [`None`] to ignore
1044    /// ## `progress_callback`
1045    ///
1046    ///   #GFileProgressCallback function for updates
1047    /// ## `progress_callback_data`
1048    /// gpointer to user data for the callback function
1049    /// ## `callback`
1050    /// a #GAsyncReadyCallback
1051    ///   to call when the request is satisfied
1052    #[cfg(feature = "v2_72")]
1053    #[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
1054    #[doc(alias = "g_file_move_async")]
1055    fn move_async<Q: FnOnce(Result<(), glib::Error>) + 'static>(
1056        &self,
1057        destination: &impl IsA<File>,
1058        flags: crate::FileCopyFlags,
1059        io_priority: glib::Priority,
1060        cancellable: Option<&impl IsA<Cancellable>>,
1061        progress_callback: Option<Box<dyn FnMut(i64, i64)>>,
1062        callback: Q,
1063    ) {
1064        let main_context = glib::MainContext::ref_thread_default();
1065        let is_main_context_owner = main_context.is_owner();
1066        let has_acquired_main_context = (!is_main_context_owner)
1067            .then(|| main_context.acquire().ok())
1068            .flatten();
1069        assert!(
1070            is_main_context_owner || has_acquired_main_context.is_some(),
1071            "Async operations only allowed if the thread is owning the MainContext"
1072        );
1073
1074        let progress_trampoline = if progress_callback.is_some() {
1075            Some(move_async_progress_trampoline::<Q> as _)
1076        } else {
1077            None
1078        };
1079
1080        let user_data: Box<(
1081            glib::thread_guard::ThreadGuard<Q>,
1082            RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
1083        )> = Box::new((
1084            glib::thread_guard::ThreadGuard::new(callback),
1085            RefCell::new(progress_callback.map(glib::thread_guard::ThreadGuard::new)),
1086        ));
1087        unsafe extern "C" fn move_async_trampoline<Q: FnOnce(Result<(), glib::Error>) + 'static>(
1088            _source_object: *mut glib::gobject_ffi::GObject,
1089            res: *mut crate::ffi::GAsyncResult,
1090            user_data: glib::ffi::gpointer,
1091        ) {
1092            unsafe {
1093                let mut error = ptr::null_mut();
1094                ffi::g_file_move_finish(_source_object as *mut _, res, &mut error);
1095                let result = if error.is_null() {
1096                    Ok(())
1097                } else {
1098                    Err(from_glib_full(error))
1099                };
1100                let callback: Box<(
1101                    glib::thread_guard::ThreadGuard<Q>,
1102                    RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
1103                )> = Box::from_raw(user_data as *mut _);
1104                let callback = callback.0.into_inner();
1105                callback(result);
1106            }
1107        }
1108        unsafe extern "C" fn move_async_progress_trampoline<
1109            Q: FnOnce(Result<(), glib::Error>) + 'static,
1110        >(
1111            current_num_bytes: i64,
1112            total_num_bytes: i64,
1113            user_data: glib::ffi::gpointer,
1114        ) {
1115            unsafe {
1116                let callback: &(
1117                    glib::thread_guard::ThreadGuard<Q>,
1118                    RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
1119                ) = &*(user_data as *const _);
1120                (callback
1121                    .1
1122                    .borrow_mut()
1123                    .as_mut()
1124                    .expect("no closure")
1125                    .get_mut())(current_num_bytes, total_num_bytes);
1126            }
1127        }
1128
1129        let user_data = Box::into_raw(user_data) as *mut _;
1130
1131        unsafe {
1132            ffi::g_file_move_async(
1133                self.as_ref().to_glib_none().0,
1134                destination.as_ref().to_glib_none().0,
1135                flags.into_glib(),
1136                io_priority.into_glib(),
1137                cancellable.map(|p| p.as_ref()).to_glib_none().0,
1138                progress_trampoline,
1139                user_data,
1140                Some(move_async_trampoline::<Q>),
1141                user_data,
1142            );
1143        }
1144    }
1145
1146    /// Asynchronously creates a symbolic link named @self which contains the
1147    /// string @symlink_value.
1148    /// ## `symlink_value`
1149    /// a string with the path for the target
1150    ///   of the new symlink
1151    /// ## `io_priority`
1152    /// the [I/O priority](iface.AsyncResult.html#io-priority) of the request
1153    /// ## `cancellable`
1154    /// optional #GCancellable object,
1155    ///   [`None`] to ignore
1156    /// ## `callback`
1157    /// a #GAsyncReadyCallback to call
1158    ///   when the request is satisfied
1159    #[cfg(feature = "v2_74")]
1160    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
1161    #[doc(alias = "g_file_make_symbolic_link_async")]
1162    fn make_symbolic_link_async<P: FnOnce(Result<(), glib::Error>) + 'static>(
1163        &self,
1164        symlink_value: impl AsRef<std::path::Path>,
1165        io_priority: glib::Priority,
1166        cancellable: Option<&impl IsA<Cancellable>>,
1167        callback: P,
1168    ) {
1169        let main_context = glib::MainContext::ref_thread_default();
1170        let is_main_context_owner = main_context.is_owner();
1171        let has_acquired_main_context = (!is_main_context_owner)
1172            .then(|| main_context.acquire().ok())
1173            .flatten();
1174        assert!(
1175            is_main_context_owner || has_acquired_main_context.is_some(),
1176            "Async operations only allowed if the thread is owning the MainContext"
1177        );
1178
1179        let user_data: Box<glib::thread_guard::ThreadGuard<P>> =
1180            Box::new(glib::thread_guard::ThreadGuard::new(callback));
1181        unsafe extern "C" fn make_symbolic_link_async_trampoline<
1182            P: FnOnce(Result<(), glib::Error>) + 'static,
1183        >(
1184            _source_object: *mut glib::gobject_ffi::GObject,
1185            res: *mut crate::ffi::GAsyncResult,
1186            user_data: glib::ffi::gpointer,
1187        ) {
1188            unsafe {
1189                let mut error = ptr::null_mut();
1190                let _ = ffi::g_file_make_symbolic_link_finish(
1191                    _source_object as *mut _,
1192                    res,
1193                    &mut error,
1194                );
1195                let result = if error.is_null() {
1196                    Ok(())
1197                } else {
1198                    Err(from_glib_full(error))
1199                };
1200                let callback: Box<glib::thread_guard::ThreadGuard<P>> =
1201                    Box::from_raw(user_data as *mut _);
1202                let callback: P = callback.into_inner();
1203                callback(result);
1204            }
1205        }
1206        let callback = make_symbolic_link_async_trampoline::<P>;
1207        unsafe {
1208            ffi::g_file_make_symbolic_link_async(
1209                self.as_ref().to_glib_none().0,
1210                symlink_value.as_ref().to_glib_none().0,
1211                io_priority.into_glib(),
1212                cancellable.map(|p| p.as_ref()).to_glib_none().0,
1213                Some(callback),
1214                Box::into_raw(user_data) as *mut _,
1215            );
1216        }
1217    }
1218
1219    #[cfg(feature = "v2_74")]
1220    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
1221    fn make_symbolic_link_future(
1222        &self,
1223        symlink_value: impl AsRef<std::path::Path>,
1224        io_priority: glib::Priority,
1225    ) -> Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>> {
1226        let symlink_value = symlink_value.as_ref().to_owned();
1227        Box::pin(crate::GioFuture::new(
1228            self,
1229            move |obj, cancellable, send| {
1230                obj.make_symbolic_link_async(
1231                    &symlink_value,
1232                    io_priority,
1233                    Some(cancellable),
1234                    move |res| {
1235                        send.resolve(res);
1236                    },
1237                );
1238            },
1239        ))
1240    }
1241
1242    #[cfg(feature = "v2_72")]
1243    #[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
1244    fn move_future(
1245        &self,
1246        destination: &(impl IsA<File> + Clone + 'static),
1247        flags: crate::FileCopyFlags,
1248        io_priority: glib::Priority,
1249    ) -> (
1250        Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>>,
1251        Pin<Box<dyn futures_core::stream::Stream<Item = (i64, i64)> + 'static>>,
1252    ) {
1253        let destination = destination.clone();
1254
1255        let (sender, receiver) = futures_channel::mpsc::unbounded();
1256
1257        let fut = Box::pin(crate::GioFuture::new(
1258            self,
1259            move |obj, cancellable, send| {
1260                obj.move_async(
1261                    &destination,
1262                    flags,
1263                    io_priority,
1264                    Some(cancellable),
1265                    Some(Box::new(move |current_num_bytes, total_num_bytes| {
1266                        let _ = sender.unbounded_send((current_num_bytes, total_num_bytes));
1267                    })),
1268                    move |res| {
1269                        send.resolve(res);
1270                    },
1271                );
1272            },
1273        ));
1274
1275        (fut, Box::pin(receiver))
1276    }
1277
1278    #[doc(alias = "g_file_set_attribute")]
1279    fn set_attribute<'a>(
1280        &self,
1281        attribute: &str,
1282        value: impl Into<FileAttributeValue<'a>>,
1283        flags: FileQueryInfoFlags,
1284        cancellable: Option<&impl IsA<Cancellable>>,
1285    ) -> Result<(), glib::Error> {
1286        unsafe {
1287            let mut error = std::ptr::null_mut();
1288            let value: FileAttributeValue<'a> = value.into();
1289            let is_ok = ffi::g_file_set_attribute(
1290                self.as_ref().to_glib_none().0,
1291                attribute.to_glib_none().0,
1292                value.type_().into_glib(),
1293                value.as_ptr(),
1294                flags.into_glib(),
1295                cancellable.map(|p| p.as_ref()).to_glib_none().0,
1296                &mut error,
1297            );
1298            debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null());
1299            if error.is_null() {
1300                Ok(())
1301            } else {
1302                Err(from_glib_full(error))
1303            }
1304        }
1305    }
1306}
1307
1308impl<O: IsA<File>> FileExtManual for O {}