[go: up one dir, main page]

gio/
file.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{cell::RefCell, mem, pin::Pin, ptr};
4
5use glib::{prelude::*, translate::*};
6
7#[cfg(feature = "v2_74")]
8use crate::FileIOStream;
9use crate::{
10    ffi, Cancellable, File, FileAttributeValue, FileCreateFlags, FileEnumerator, FileQueryInfoFlags,
11};
12
13impl File {
14    #[cfg(feature = "v2_74")]
15    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
16    #[doc(alias = "g_file_new_tmp_async")]
17    pub fn new_tmp_async<P: FnOnce(Result<(File, FileIOStream), glib::Error>) + 'static>(
18        tmpl: Option<impl AsRef<std::path::Path>>,
19        io_priority: glib::Priority,
20        cancellable: Option<&impl IsA<Cancellable>>,
21        callback: P,
22    ) {
23        let main_context = glib::MainContext::ref_thread_default();
24        let is_main_context_owner = main_context.is_owner();
25        let has_acquired_main_context = (!is_main_context_owner)
26            .then(|| main_context.acquire().ok())
27            .flatten();
28        assert!(
29            is_main_context_owner || has_acquired_main_context.is_some(),
30            "Async operations only allowed if the thread is owning the MainContext"
31        );
32
33        let user_data: Box<glib::thread_guard::ThreadGuard<P>> =
34            Box::new(glib::thread_guard::ThreadGuard::new(callback));
35        unsafe extern "C" fn new_tmp_async_trampoline<
36            P: FnOnce(Result<(File, FileIOStream), glib::Error>) + 'static,
37        >(
38            _source_object: *mut glib::gobject_ffi::GObject,
39            res: *mut crate::ffi::GAsyncResult,
40            user_data: glib::ffi::gpointer,
41        ) {
42            let mut error = ptr::null_mut();
43            let mut iostream = ptr::null_mut();
44            let ret = ffi::g_file_new_tmp_finish(res, &mut iostream, &mut error);
45            let result = if error.is_null() {
46                Ok((from_glib_full(ret), from_glib_full(iostream)))
47            } else {
48                Err(from_glib_full(error))
49            };
50            let callback: Box<glib::thread_guard::ThreadGuard<P>> =
51                Box::from_raw(user_data as *mut _);
52            let callback: P = callback.into_inner();
53            callback(result);
54        }
55        let callback = new_tmp_async_trampoline::<P>;
56        unsafe {
57            ffi::g_file_new_tmp_async(
58                tmpl.as_ref().map(|p| p.as_ref()).to_glib_none().0,
59                io_priority.into_glib(),
60                cancellable.map(|p| p.as_ref()).to_glib_none().0,
61                Some(callback),
62                Box::into_raw(user_data) as *mut _,
63            );
64        }
65    }
66
67    #[cfg(feature = "v2_74")]
68    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
69    pub fn new_tmp_future(
70        tmpl: Option<impl AsRef<std::path::Path>>,
71        io_priority: glib::Priority,
72    ) -> Pin<
73        Box<dyn std::future::Future<Output = Result<(File, FileIOStream), glib::Error>> + 'static>,
74    > {
75        let tmpl = tmpl.map(|tmpl| tmpl.as_ref().to_owned());
76        Box::pin(crate::GioFuture::new(
77            &(),
78            move |_obj, cancellable, send| {
79                Self::new_tmp_async(
80                    tmpl.as_ref()
81                        .map(<std::path::PathBuf as std::borrow::Borrow<std::path::Path>>::borrow),
82                    io_priority,
83                    Some(cancellable),
84                    move |res| {
85                        send.resolve(res);
86                    },
87                );
88            },
89        ))
90    }
91
92    #[cfg(feature = "v2_74")]
93    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
94    #[doc(alias = "g_file_new_tmp_dir_async")]
95    pub fn new_tmp_dir_async<P: FnOnce(Result<File, glib::Error>) + 'static>(
96        tmpl: Option<impl AsRef<std::path::Path>>,
97        io_priority: glib::Priority,
98        cancellable: Option<&impl IsA<Cancellable>>,
99        callback: P,
100    ) {
101        let main_context = glib::MainContext::ref_thread_default();
102        let is_main_context_owner = main_context.is_owner();
103        let has_acquired_main_context = (!is_main_context_owner)
104            .then(|| main_context.acquire().ok())
105            .flatten();
106        assert!(
107            is_main_context_owner || has_acquired_main_context.is_some(),
108            "Async operations only allowed if the thread is owning the MainContext"
109        );
110
111        let user_data: Box<glib::thread_guard::ThreadGuard<P>> =
112            Box::new(glib::thread_guard::ThreadGuard::new(callback));
113        unsafe extern "C" fn new_tmp_dir_async_trampoline<
114            P: FnOnce(Result<File, glib::Error>) + 'static,
115        >(
116            _source_object: *mut glib::gobject_ffi::GObject,
117            res: *mut crate::ffi::GAsyncResult,
118            user_data: glib::ffi::gpointer,
119        ) {
120            let mut error = ptr::null_mut();
121            let ret = ffi::g_file_new_tmp_dir_finish(res, &mut error);
122            let result = if error.is_null() {
123                Ok(from_glib_full(ret))
124            } else {
125                Err(from_glib_full(error))
126            };
127            let callback: Box<glib::thread_guard::ThreadGuard<P>> =
128                Box::from_raw(user_data as *mut _);
129            let callback: P = callback.into_inner();
130            callback(result);
131        }
132        let callback = new_tmp_dir_async_trampoline::<P>;
133        unsafe {
134            ffi::g_file_new_tmp_dir_async(
135                tmpl.as_ref().map(|p| p.as_ref()).to_glib_none().0,
136                io_priority.into_glib(),
137                cancellable.map(|p| p.as_ref()).to_glib_none().0,
138                Some(callback),
139                Box::into_raw(user_data) as *mut _,
140            );
141        }
142    }
143
144    #[cfg(feature = "v2_74")]
145    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
146    pub fn new_tmp_dir_future(
147        tmpl: Option<impl AsRef<std::path::Path>>,
148        io_priority: glib::Priority,
149    ) -> Pin<Box<dyn std::future::Future<Output = Result<File, glib::Error>> + 'static>> {
150        let tmpl = tmpl.map(|tmpl| tmpl.as_ref().to_owned());
151        Box::pin(crate::GioFuture::new(
152            &(),
153            move |_obj, cancellable, send| {
154                Self::new_tmp_dir_async(
155                    tmpl.as_ref()
156                        .map(<std::path::PathBuf as std::borrow::Borrow<std::path::Path>>::borrow),
157                    io_priority,
158                    Some(cancellable),
159                    move |res| {
160                        send.resolve(res);
161                    },
162                );
163            },
164        ))
165    }
166}
167
168pub trait FileExtManual: IsA<File> + Sized {
169    #[doc(alias = "g_file_replace_contents_async")]
170    fn replace_contents_async<
171        B: AsRef<[u8]> + Send + 'static,
172        R: FnOnce(Result<(B, Option<glib::GString>), (B, glib::Error)>) + 'static,
173        C: IsA<Cancellable>,
174    >(
175        &self,
176        contents: B,
177        etag: Option<&str>,
178        make_backup: bool,
179        flags: FileCreateFlags,
180        cancellable: Option<&C>,
181        callback: R,
182    ) {
183        let main_context = glib::MainContext::ref_thread_default();
184        let is_main_context_owner = main_context.is_owner();
185        let has_acquired_main_context = (!is_main_context_owner)
186            .then(|| main_context.acquire().ok())
187            .flatten();
188        assert!(
189            is_main_context_owner || has_acquired_main_context.is_some(),
190            "Async operations only allowed if the thread is owning the MainContext"
191        );
192
193        let etag = etag.to_glib_none();
194        let cancellable = cancellable.map(|c| c.as_ref());
195        let gcancellable = cancellable.to_glib_none();
196        let user_data: Box<(glib::thread_guard::ThreadGuard<R>, B)> =
197            Box::new((glib::thread_guard::ThreadGuard::new(callback), contents));
198        // Need to do this after boxing as the contents pointer might change by moving into the box
199        let (count, contents_ptr) = {
200            let contents = &user_data.1;
201            let slice = contents.as_ref();
202            (slice.len(), slice.as_ptr())
203        };
204        unsafe extern "C" fn replace_contents_async_trampoline<
205            B: AsRef<[u8]> + Send + 'static,
206            R: FnOnce(Result<(B, Option<glib::GString>), (B, glib::Error)>) + 'static,
207        >(
208            _source_object: *mut glib::gobject_ffi::GObject,
209            res: *mut ffi::GAsyncResult,
210            user_data: glib::ffi::gpointer,
211        ) {
212            let user_data: Box<(glib::thread_guard::ThreadGuard<R>, B)> =
213                Box::from_raw(user_data as *mut _);
214            let (callback, contents) = *user_data;
215            let callback = callback.into_inner();
216
217            let mut error = ptr::null_mut();
218            let mut new_etag = ptr::null_mut();
219            let _ = ffi::g_file_replace_contents_finish(
220                _source_object as *mut _,
221                res,
222                &mut new_etag,
223                &mut error,
224            );
225            let result = if error.is_null() {
226                Ok((contents, from_glib_full(new_etag)))
227            } else {
228                Err((contents, from_glib_full(error)))
229            };
230            callback(result);
231        }
232        let callback = replace_contents_async_trampoline::<B, R>;
233        unsafe {
234            ffi::g_file_replace_contents_async(
235                self.as_ref().to_glib_none().0,
236                mut_override(contents_ptr),
237                count,
238                etag.0,
239                make_backup.into_glib(),
240                flags.into_glib(),
241                gcancellable.0,
242                Some(callback),
243                Box::into_raw(user_data) as *mut _,
244            );
245        }
246    }
247
248    fn replace_contents_future<B: AsRef<[u8]> + Send + 'static>(
249        &self,
250        contents: B,
251        etag: Option<&str>,
252        make_backup: bool,
253        flags: FileCreateFlags,
254    ) -> Pin<
255        Box<
256            dyn std::future::Future<Output = Result<(B, Option<glib::GString>), (B, glib::Error)>>
257                + 'static,
258        >,
259    > {
260        let etag = etag.map(glib::GString::from);
261        Box::pin(crate::GioFuture::new(
262            self,
263            move |obj, cancellable, send| {
264                obj.replace_contents_async(
265                    contents,
266                    etag.as_ref().map(|s| s.as_str()),
267                    make_backup,
268                    flags,
269                    Some(cancellable),
270                    move |res| {
271                        send.resolve(res);
272                    },
273                );
274            },
275        ))
276    }
277
278    #[doc(alias = "g_file_enumerate_children_async")]
279    fn enumerate_children_async<
280        P: IsA<Cancellable>,
281        Q: FnOnce(Result<FileEnumerator, glib::Error>) + 'static,
282    >(
283        &self,
284        attributes: &str,
285        flags: FileQueryInfoFlags,
286        io_priority: glib::Priority,
287        cancellable: Option<&P>,
288        callback: Q,
289    ) {
290        let main_context = glib::MainContext::ref_thread_default();
291        let is_main_context_owner = main_context.is_owner();
292        let has_acquired_main_context = (!is_main_context_owner)
293            .then(|| main_context.acquire().ok())
294            .flatten();
295        assert!(
296            is_main_context_owner || has_acquired_main_context.is_some(),
297            "Async operations only allowed if the thread is owning the MainContext"
298        );
299
300        let user_data: Box<glib::thread_guard::ThreadGuard<Q>> =
301            Box::new(glib::thread_guard::ThreadGuard::new(callback));
302        unsafe extern "C" fn create_async_trampoline<
303            Q: FnOnce(Result<FileEnumerator, glib::Error>) + 'static,
304        >(
305            _source_object: *mut glib::gobject_ffi::GObject,
306            res: *mut crate::ffi::GAsyncResult,
307            user_data: glib::ffi::gpointer,
308        ) {
309            let mut error = ptr::null_mut();
310            let ret =
311                ffi::g_file_enumerate_children_finish(_source_object as *mut _, res, &mut error);
312            let result = if error.is_null() {
313                Ok(from_glib_full(ret))
314            } else {
315                Err(from_glib_full(error))
316            };
317            let callback: Box<glib::thread_guard::ThreadGuard<Q>> =
318                Box::from_raw(user_data as *mut _);
319            let callback = callback.into_inner();
320            callback(result);
321        }
322        let callback = create_async_trampoline::<Q>;
323        unsafe {
324            ffi::g_file_enumerate_children_async(
325                self.as_ref().to_glib_none().0,
326                attributes.to_glib_none().0,
327                flags.into_glib(),
328                io_priority.into_glib(),
329                cancellable.map(|p| p.as_ref()).to_glib_none().0,
330                Some(callback),
331                Box::into_raw(user_data) as *mut _,
332            );
333        }
334    }
335
336    fn enumerate_children_future(
337        &self,
338        attributes: &str,
339        flags: FileQueryInfoFlags,
340        io_priority: glib::Priority,
341    ) -> Pin<Box<dyn std::future::Future<Output = Result<FileEnumerator, glib::Error>> + 'static>>
342    {
343        let attributes = attributes.to_owned();
344        Box::pin(crate::GioFuture::new(
345            self,
346            move |obj, cancellable, send| {
347                obj.enumerate_children_async(
348                    &attributes,
349                    flags,
350                    io_priority,
351                    Some(cancellable),
352                    move |res| {
353                        send.resolve(res);
354                    },
355                );
356            },
357        ))
358    }
359
360    #[doc(alias = "g_file_copy_async")]
361    fn copy_async<Q: FnOnce(Result<(), glib::Error>) + 'static>(
362        &self,
363        destination: &impl IsA<File>,
364        flags: crate::FileCopyFlags,
365        io_priority: glib::Priority,
366        cancellable: Option<&impl IsA<Cancellable>>,
367        progress_callback: Option<Box<dyn FnMut(i64, i64)>>,
368        callback: Q,
369    ) {
370        let main_context = glib::MainContext::ref_thread_default();
371        let is_main_context_owner = main_context.is_owner();
372        let has_acquired_main_context = (!is_main_context_owner)
373            .then(|| main_context.acquire().ok())
374            .flatten();
375        assert!(
376            is_main_context_owner || has_acquired_main_context.is_some(),
377            "Async operations only allowed if the thread is owning the MainContext"
378        );
379
380        let progress_trampoline = if progress_callback.is_some() {
381            Some(copy_async_progress_trampoline::<Q> as _)
382        } else {
383            None
384        };
385
386        let user_data: Box<(
387            glib::thread_guard::ThreadGuard<Q>,
388            RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
389        )> = Box::new((
390            glib::thread_guard::ThreadGuard::new(callback),
391            RefCell::new(progress_callback.map(glib::thread_guard::ThreadGuard::new)),
392        ));
393        unsafe extern "C" fn copy_async_trampoline<Q: FnOnce(Result<(), glib::Error>) + 'static>(
394            _source_object: *mut glib::gobject_ffi::GObject,
395            res: *mut crate::ffi::GAsyncResult,
396            user_data: glib::ffi::gpointer,
397        ) {
398            let mut error = ptr::null_mut();
399            ffi::g_file_copy_finish(_source_object as *mut _, res, &mut error);
400            let result = if error.is_null() {
401                Ok(())
402            } else {
403                Err(from_glib_full(error))
404            };
405            let callback: Box<(
406                glib::thread_guard::ThreadGuard<Q>,
407                RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
408            )> = Box::from_raw(user_data as *mut _);
409            let callback = callback.0.into_inner();
410            callback(result);
411        }
412        unsafe extern "C" fn copy_async_progress_trampoline<
413            Q: FnOnce(Result<(), glib::Error>) + 'static,
414        >(
415            current_num_bytes: i64,
416            total_num_bytes: i64,
417            user_data: glib::ffi::gpointer,
418        ) {
419            let callback: &(
420                glib::thread_guard::ThreadGuard<Q>,
421                RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
422            ) = &*(user_data as *const _);
423            (callback
424                .1
425                .borrow_mut()
426                .as_mut()
427                .expect("no closure")
428                .get_mut())(current_num_bytes, total_num_bytes);
429        }
430
431        let user_data = Box::into_raw(user_data) as *mut _;
432
433        unsafe {
434            ffi::g_file_copy_async(
435                self.as_ref().to_glib_none().0,
436                destination.as_ref().to_glib_none().0,
437                flags.into_glib(),
438                io_priority.into_glib(),
439                cancellable.map(|p| p.as_ref()).to_glib_none().0,
440                progress_trampoline,
441                user_data,
442                Some(copy_async_trampoline::<Q>),
443                user_data,
444            );
445        }
446    }
447
448    fn copy_future(
449        &self,
450        destination: &(impl IsA<File> + Clone + 'static),
451        flags: crate::FileCopyFlags,
452        io_priority: glib::Priority,
453    ) -> (
454        Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>>,
455        Pin<Box<dyn futures_core::stream::Stream<Item = (i64, i64)> + 'static>>,
456    ) {
457        let destination = destination.clone();
458
459        let (sender, receiver) = futures_channel::mpsc::unbounded();
460
461        let fut = Box::pin(crate::GioFuture::new(
462            self,
463            move |obj, cancellable, send| {
464                obj.copy_async(
465                    &destination,
466                    flags,
467                    io_priority,
468                    Some(cancellable),
469                    Some(Box::new(move |current_num_bytes, total_num_bytes| {
470                        let _ = sender.unbounded_send((current_num_bytes, total_num_bytes));
471                    })),
472                    move |res| {
473                        send.resolve(res);
474                    },
475                );
476            },
477        ));
478
479        (fut, Box::pin(receiver))
480    }
481
482    #[doc(alias = "g_file_load_contents")]
483    fn load_contents(
484        &self,
485        cancellable: Option<&impl IsA<Cancellable>>,
486    ) -> Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error> {
487        unsafe {
488            let mut contents = std::ptr::null_mut();
489            let mut length = std::mem::MaybeUninit::uninit();
490            let mut etag_out = std::ptr::null_mut();
491            let mut error = std::ptr::null_mut();
492            let is_ok = ffi::g_file_load_contents(
493                self.as_ref().to_glib_none().0,
494                cancellable.map(|p| p.as_ref()).to_glib_none().0,
495                &mut contents,
496                length.as_mut_ptr(),
497                &mut etag_out,
498                &mut error,
499            );
500            debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null());
501            if error.is_null() {
502                Ok((
503                    FromGlibContainer::from_glib_full_num(contents, length.assume_init() as _),
504                    from_glib_full(etag_out),
505                ))
506            } else {
507                Err(from_glib_full(error))
508            }
509        }
510    }
511
512    #[doc(alias = "g_file_load_contents_async")]
513    fn load_contents_async<
514        P: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
515            + 'static,
516    >(
517        &self,
518        cancellable: Option<&impl IsA<Cancellable>>,
519        callback: P,
520    ) {
521        let main_context = glib::MainContext::ref_thread_default();
522        let is_main_context_owner = main_context.is_owner();
523        let has_acquired_main_context = (!is_main_context_owner)
524            .then(|| main_context.acquire().ok())
525            .flatten();
526        assert!(
527            is_main_context_owner || has_acquired_main_context.is_some(),
528            "Async operations only allowed if the thread is owning the MainContext"
529        );
530
531        let user_data: Box<glib::thread_guard::ThreadGuard<P>> =
532            Box::new(glib::thread_guard::ThreadGuard::new(callback));
533        unsafe extern "C" fn load_contents_async_trampoline<
534            P: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
535                + 'static,
536        >(
537            _source_object: *mut glib::gobject_ffi::GObject,
538            res: *mut crate::ffi::GAsyncResult,
539            user_data: glib::ffi::gpointer,
540        ) {
541            let mut error = std::ptr::null_mut();
542            let mut contents = std::ptr::null_mut();
543            let mut length = std::mem::MaybeUninit::uninit();
544            let mut etag_out = std::ptr::null_mut();
545            let _ = ffi::g_file_load_contents_finish(
546                _source_object as *mut _,
547                res,
548                &mut contents,
549                length.as_mut_ptr(),
550                &mut etag_out,
551                &mut error,
552            );
553            let result = if error.is_null() {
554                Ok((
555                    FromGlibContainer::from_glib_full_num(contents, length.assume_init() as _),
556                    from_glib_full(etag_out),
557                ))
558            } else {
559                Err(from_glib_full(error))
560            };
561            let callback: Box<glib::thread_guard::ThreadGuard<P>> =
562                Box::from_raw(user_data as *mut _);
563            let callback: P = callback.into_inner();
564            callback(result);
565        }
566        let callback = load_contents_async_trampoline::<P>;
567        unsafe {
568            ffi::g_file_load_contents_async(
569                self.as_ref().to_glib_none().0,
570                cancellable.map(|p| p.as_ref()).to_glib_none().0,
571                Some(callback),
572                Box::into_raw(user_data) as *mut _,
573            );
574        }
575    }
576
577    fn load_contents_future(
578        &self,
579    ) -> Pin<
580        Box<
581            dyn std::future::Future<
582                    Output = Result<
583                        (glib::collections::Slice<u8>, Option<glib::GString>),
584                        glib::Error,
585                    >,
586                > + 'static,
587        >,
588    > {
589        Box::pin(crate::GioFuture::new(
590            self,
591            move |obj, cancellable, send| {
592                obj.load_contents_async(Some(cancellable), move |res| {
593                    send.resolve(res);
594                });
595            },
596        ))
597    }
598
599    #[doc(alias = "g_file_load_partial_contents_async")]
600    fn load_partial_contents_async<
601        P: FnMut(&[u8]) -> bool + 'static,
602        Q: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
603            + 'static,
604    >(
605        &self,
606        cancellable: Option<&impl IsA<Cancellable>>,
607        read_more_callback: P,
608        callback: Q,
609    ) {
610        let main_context = glib::MainContext::ref_thread_default();
611        let is_main_context_owner = main_context.is_owner();
612        let has_acquired_main_context = (!is_main_context_owner)
613            .then(|| main_context.acquire().ok())
614            .flatten();
615        assert!(
616            is_main_context_owner || has_acquired_main_context.is_some(),
617            "Async operations only allowed if the thread is owning the MainContext"
618        );
619
620        let user_data: Box<(
621            glib::thread_guard::ThreadGuard<Q>,
622            RefCell<glib::thread_guard::ThreadGuard<P>>,
623        )> = Box::new((
624            glib::thread_guard::ThreadGuard::new(callback),
625            RefCell::new(glib::thread_guard::ThreadGuard::new(read_more_callback)),
626        ));
627        unsafe extern "C" fn load_partial_contents_async_trampoline<
628            P: FnMut(&[u8]) -> bool + 'static,
629            Q: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
630                + 'static,
631        >(
632            _source_object: *mut glib::gobject_ffi::GObject,
633            res: *mut crate::ffi::GAsyncResult,
634            user_data: glib::ffi::gpointer,
635        ) {
636            let mut contents = ptr::null_mut();
637            let mut length = mem::MaybeUninit::uninit();
638            let mut etag_out = ptr::null_mut();
639            let mut error = ptr::null_mut();
640            ffi::g_file_load_partial_contents_finish(
641                _source_object as *mut _,
642                res,
643                &mut contents,
644                length.as_mut_ptr(),
645                &mut etag_out,
646                &mut error,
647            );
648            let result = if error.is_null() {
649                Ok((
650                    FromGlibContainer::from_glib_full_num(contents, length.assume_init() as _),
651                    from_glib_full(etag_out),
652                ))
653            } else {
654                Err(from_glib_full(error))
655            };
656            let callback: Box<(
657                glib::thread_guard::ThreadGuard<Q>,
658                RefCell<glib::thread_guard::ThreadGuard<P>>,
659            )> = Box::from_raw(user_data as *mut _);
660            let callback = callback.0.into_inner();
661            callback(result);
662        }
663        unsafe extern "C" fn load_partial_contents_async_read_more_trampoline<
664            P: FnMut(&[u8]) -> bool + 'static,
665            Q: FnOnce(Result<(glib::collections::Slice<u8>, Option<glib::GString>), glib::Error>)
666                + 'static,
667        >(
668            file_contents: *const libc::c_char,
669            file_size: i64,
670            user_data: glib::ffi::gpointer,
671        ) -> glib::ffi::gboolean {
672            use std::slice;
673
674            let callback: &(
675                glib::thread_guard::ThreadGuard<Q>,
676                RefCell<glib::thread_guard::ThreadGuard<P>>,
677            ) = &*(user_data as *const _);
678            let data = if file_size == 0 {
679                &[]
680            } else {
681                slice::from_raw_parts(file_contents as *const u8, file_size as usize)
682            };
683
684            (*callback.1.borrow_mut().get_mut())(data).into_glib()
685        }
686
687        let user_data = Box::into_raw(user_data) as *mut _;
688
689        unsafe {
690            ffi::g_file_load_partial_contents_async(
691                self.as_ref().to_glib_none().0,
692                cancellable.map(|p| p.as_ref()).to_glib_none().0,
693                Some(load_partial_contents_async_read_more_trampoline::<P, Q>),
694                Some(load_partial_contents_async_trampoline::<P, Q>),
695                user_data,
696            );
697        }
698    }
699
700    #[doc(alias = "g_file_measure_disk_usage")]
701    fn measure_disk_usage(
702        &self,
703        flags: crate::FileMeasureFlags,
704        cancellable: Option<&impl IsA<Cancellable>>,
705        progress_callback: Option<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>,
706    ) -> Result<(u64, u64, u64), glib::Error> {
707        let progress_callback_data: Box<
708            Option<RefCell<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>>,
709        > = Box::new(progress_callback.map(RefCell::new));
710        unsafe extern "C" fn progress_callback_func(
711            reporting: glib::ffi::gboolean,
712            current_size: u64,
713            num_dirs: u64,
714            num_files: u64,
715            user_data: glib::ffi::gpointer,
716        ) {
717            let reporting = from_glib(reporting);
718            let callback: &Option<RefCell<Box<dyn Fn(bool, u64, u64, u64) + 'static>>> =
719                &*(user_data as *mut _);
720            if let Some(ref callback) = *callback {
721                (*callback.borrow_mut())(reporting, current_size, num_dirs, num_files)
722            } else {
723                panic!("cannot get closure...")
724            };
725        }
726        let progress_callback = if progress_callback_data.is_some() {
727            Some(progress_callback_func as _)
728        } else {
729            None
730        };
731        let super_callback0: Box<Option<RefCell<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>>> =
732            progress_callback_data;
733        unsafe {
734            let mut disk_usage = mem::MaybeUninit::uninit();
735            let mut num_dirs = mem::MaybeUninit::uninit();
736            let mut num_files = mem::MaybeUninit::uninit();
737            let mut error = ptr::null_mut();
738            let _ = ffi::g_file_measure_disk_usage(
739                self.as_ref().to_glib_none().0,
740                flags.into_glib(),
741                cancellable.map(|p| p.as_ref()).to_glib_none().0,
742                progress_callback,
743                Box::into_raw(super_callback0) as *mut _,
744                disk_usage.as_mut_ptr(),
745                num_dirs.as_mut_ptr(),
746                num_files.as_mut_ptr(),
747                &mut error,
748            );
749            let disk_usage = disk_usage.assume_init();
750            let num_dirs = num_dirs.assume_init();
751            let num_files = num_files.assume_init();
752            if error.is_null() {
753                Ok((disk_usage, num_dirs, num_files))
754            } else {
755                Err(from_glib_full(error))
756            }
757        }
758    }
759
760    #[doc(alias = "g_file_measure_disk_usage_async")]
761    fn measure_disk_usage_async<P: FnOnce(Result<(u64, u64, u64), glib::Error>) + 'static>(
762        &self,
763        flags: crate::FileMeasureFlags,
764        io_priority: glib::Priority,
765        cancellable: Option<&impl IsA<Cancellable>>,
766        progress_callback: Option<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>,
767        callback: P,
768    ) {
769        let main_context = glib::MainContext::ref_thread_default();
770        let is_main_context_owner = main_context.is_owner();
771        let has_acquired_main_context = (!is_main_context_owner)
772            .then(|| main_context.acquire().ok())
773            .flatten();
774        assert!(
775            is_main_context_owner || has_acquired_main_context.is_some(),
776            "Async operations only allowed if the thread is owning the MainContext"
777        );
778
779        let progress_callback_trampoline = if progress_callback.is_some() {
780            Some(measure_disk_usage_async_progress_trampoline::<P> as _)
781        } else {
782            None
783        };
784
785        let user_data: Box<(
786            glib::thread_guard::ThreadGuard<P>,
787            RefCell<
788                Option<
789                    glib::thread_guard::ThreadGuard<Box<dyn FnMut(bool, u64, u64, u64) + 'static>>,
790                >,
791            >,
792        )> = Box::new((
793            glib::thread_guard::ThreadGuard::new(callback),
794            RefCell::new(progress_callback.map(glib::thread_guard::ThreadGuard::new)),
795        ));
796        unsafe extern "C" fn measure_disk_usage_async_trampoline<
797            P: FnOnce(Result<(u64, u64, u64), glib::Error>) + 'static,
798        >(
799            _source_object: *mut glib::gobject_ffi::GObject,
800            res: *mut crate::ffi::GAsyncResult,
801            user_data: glib::ffi::gpointer,
802        ) {
803            let mut disk_usage = mem::MaybeUninit::uninit();
804            let mut num_dirs = mem::MaybeUninit::uninit();
805            let mut num_files = mem::MaybeUninit::uninit();
806            let mut error = ptr::null_mut();
807            ffi::g_file_measure_disk_usage_finish(
808                _source_object as *mut _,
809                res,
810                disk_usage.as_mut_ptr(),
811                num_dirs.as_mut_ptr(),
812                num_files.as_mut_ptr(),
813                &mut error,
814            );
815            let result = if error.is_null() {
816                Ok((
817                    disk_usage.assume_init(),
818                    num_dirs.assume_init(),
819                    num_files.assume_init(),
820                ))
821            } else {
822                Err(from_glib_full(error))
823            };
824            let callback: Box<(
825                glib::thread_guard::ThreadGuard<P>,
826                RefCell<
827                    Option<
828                        glib::thread_guard::ThreadGuard<
829                            Box<dyn FnMut(bool, u64, u64, u64) + 'static>,
830                        >,
831                    >,
832                >,
833            )> = Box::from_raw(user_data as *mut _);
834            let callback = callback.0.into_inner();
835            callback(result);
836        }
837        unsafe extern "C" fn measure_disk_usage_async_progress_trampoline<
838            P: FnOnce(Result<(u64, u64, u64), glib::Error>) + 'static,
839        >(
840            reporting: glib::ffi::gboolean,
841            disk_usage: u64,
842            num_dirs: u64,
843            num_files: u64,
844            user_data: glib::ffi::gpointer,
845        ) {
846            let callback: &(
847                glib::thread_guard::ThreadGuard<P>,
848                RefCell<
849                    Option<
850                        glib::thread_guard::ThreadGuard<
851                            Box<dyn FnMut(bool, u64, u64, u64) + 'static>,
852                        >,
853                    >,
854                >,
855            ) = &*(user_data as *const _);
856            (callback
857                .1
858                .borrow_mut()
859                .as_mut()
860                .expect("can't get callback")
861                .get_mut())(from_glib(reporting), disk_usage, num_dirs, num_files);
862        }
863
864        let user_data = Box::into_raw(user_data) as *mut _;
865
866        unsafe {
867            ffi::g_file_measure_disk_usage_async(
868                self.as_ref().to_glib_none().0,
869                flags.into_glib(),
870                io_priority.into_glib(),
871                cancellable.map(|p| p.as_ref()).to_glib_none().0,
872                progress_callback_trampoline,
873                user_data,
874                Some(measure_disk_usage_async_trampoline::<P>),
875                user_data,
876            );
877        }
878    }
879
880    fn measure_disk_usage_future(
881        &self,
882        flags: crate::FileMeasureFlags,
883        io_priority: glib::Priority,
884    ) -> (
885        Pin<Box<dyn std::future::Future<Output = Result<(u64, u64, u64), glib::Error>> + 'static>>,
886        Pin<Box<dyn futures_core::stream::Stream<Item = (bool, u64, u64, u64)> + 'static>>,
887    ) {
888        let (sender, receiver) = futures_channel::mpsc::unbounded();
889
890        let fut = Box::pin(crate::GioFuture::new(
891            self,
892            move |obj, cancellable, send| {
893                obj.measure_disk_usage_async(
894                    flags,
895                    io_priority,
896                    Some(cancellable),
897                    Some(Box::new(
898                        move |reporting, disk_usage, num_dirs, num_files| {
899                            let _ =
900                                sender.unbounded_send((reporting, disk_usage, num_dirs, num_files));
901                        },
902                    )),
903                    move |res| {
904                        send.resolve(res);
905                    },
906                );
907            },
908        ));
909
910        (fut, Box::pin(receiver))
911    }
912
913    #[cfg(feature = "v2_72")]
914    #[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
915    #[doc(alias = "g_file_move_async")]
916    fn move_async<Q: FnOnce(Result<(), glib::Error>) + 'static>(
917        &self,
918        destination: &impl IsA<File>,
919        flags: crate::FileCopyFlags,
920        io_priority: glib::Priority,
921        cancellable: Option<&impl IsA<Cancellable>>,
922        progress_callback: Option<Box<dyn FnMut(i64, i64)>>,
923        callback: Q,
924    ) {
925        let main_context = glib::MainContext::ref_thread_default();
926        let is_main_context_owner = main_context.is_owner();
927        let has_acquired_main_context = (!is_main_context_owner)
928            .then(|| main_context.acquire().ok())
929            .flatten();
930        assert!(
931            is_main_context_owner || has_acquired_main_context.is_some(),
932            "Async operations only allowed if the thread is owning the MainContext"
933        );
934
935        let progress_trampoline = if progress_callback.is_some() {
936            Some(move_async_progress_trampoline::<Q> as _)
937        } else {
938            None
939        };
940
941        let user_data: Box<(
942            glib::thread_guard::ThreadGuard<Q>,
943            RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
944        )> = Box::new((
945            glib::thread_guard::ThreadGuard::new(callback),
946            RefCell::new(progress_callback.map(glib::thread_guard::ThreadGuard::new)),
947        ));
948        unsafe extern "C" fn move_async_trampoline<Q: FnOnce(Result<(), glib::Error>) + 'static>(
949            _source_object: *mut glib::gobject_ffi::GObject,
950            res: *mut crate::ffi::GAsyncResult,
951            user_data: glib::ffi::gpointer,
952        ) {
953            let mut error = ptr::null_mut();
954            ffi::g_file_move_finish(_source_object as *mut _, res, &mut error);
955            let result = if error.is_null() {
956                Ok(())
957            } else {
958                Err(from_glib_full(error))
959            };
960            let callback: Box<(
961                glib::thread_guard::ThreadGuard<Q>,
962                RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
963            )> = Box::from_raw(user_data as *mut _);
964            let callback = callback.0.into_inner();
965            callback(result);
966        }
967        unsafe extern "C" fn move_async_progress_trampoline<
968            Q: FnOnce(Result<(), glib::Error>) + 'static,
969        >(
970            current_num_bytes: i64,
971            total_num_bytes: i64,
972            user_data: glib::ffi::gpointer,
973        ) {
974            let callback: &(
975                glib::thread_guard::ThreadGuard<Q>,
976                RefCell<Option<glib::thread_guard::ThreadGuard<Box<dyn FnMut(i64, i64)>>>>,
977            ) = &*(user_data as *const _);
978            (callback
979                .1
980                .borrow_mut()
981                .as_mut()
982                .expect("no closure")
983                .get_mut())(current_num_bytes, total_num_bytes);
984        }
985
986        let user_data = Box::into_raw(user_data) as *mut _;
987
988        unsafe {
989            ffi::g_file_move_async(
990                self.as_ref().to_glib_none().0,
991                destination.as_ref().to_glib_none().0,
992                flags.into_glib(),
993                io_priority.into_glib(),
994                cancellable.map(|p| p.as_ref()).to_glib_none().0,
995                progress_trampoline,
996                user_data,
997                Some(move_async_trampoline::<Q>),
998                user_data,
999            );
1000        }
1001    }
1002
1003    #[cfg(feature = "v2_74")]
1004    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
1005    #[doc(alias = "g_file_make_symbolic_link_async")]
1006    fn make_symbolic_link_async<P: FnOnce(Result<(), glib::Error>) + 'static>(
1007        &self,
1008        symlink_value: impl AsRef<std::path::Path>,
1009        io_priority: glib::Priority,
1010        cancellable: Option<&impl IsA<Cancellable>>,
1011        callback: P,
1012    ) {
1013        let main_context = glib::MainContext::ref_thread_default();
1014        let is_main_context_owner = main_context.is_owner();
1015        let has_acquired_main_context = (!is_main_context_owner)
1016            .then(|| main_context.acquire().ok())
1017            .flatten();
1018        assert!(
1019            is_main_context_owner || has_acquired_main_context.is_some(),
1020            "Async operations only allowed if the thread is owning the MainContext"
1021        );
1022
1023        let user_data: Box<glib::thread_guard::ThreadGuard<P>> =
1024            Box::new(glib::thread_guard::ThreadGuard::new(callback));
1025        unsafe extern "C" fn make_symbolic_link_async_trampoline<
1026            P: FnOnce(Result<(), glib::Error>) + 'static,
1027        >(
1028            _source_object: *mut glib::gobject_ffi::GObject,
1029            res: *mut crate::ffi::GAsyncResult,
1030            user_data: glib::ffi::gpointer,
1031        ) {
1032            let mut error = ptr::null_mut();
1033            let _ =
1034                ffi::g_file_make_symbolic_link_finish(_source_object as *mut _, res, &mut error);
1035            let result = if error.is_null() {
1036                Ok(())
1037            } else {
1038                Err(from_glib_full(error))
1039            };
1040            let callback: Box<glib::thread_guard::ThreadGuard<P>> =
1041                Box::from_raw(user_data as *mut _);
1042            let callback: P = callback.into_inner();
1043            callback(result);
1044        }
1045        let callback = make_symbolic_link_async_trampoline::<P>;
1046        unsafe {
1047            ffi::g_file_make_symbolic_link_async(
1048                self.as_ref().to_glib_none().0,
1049                symlink_value.as_ref().to_glib_none().0,
1050                io_priority.into_glib(),
1051                cancellable.map(|p| p.as_ref()).to_glib_none().0,
1052                Some(callback),
1053                Box::into_raw(user_data) as *mut _,
1054            );
1055        }
1056    }
1057
1058    #[cfg(feature = "v2_74")]
1059    #[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
1060    fn make_symbolic_link_future(
1061        &self,
1062        symlink_value: impl AsRef<std::path::Path>,
1063        io_priority: glib::Priority,
1064    ) -> Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>> {
1065        let symlink_value = symlink_value.as_ref().to_owned();
1066        Box::pin(crate::GioFuture::new(
1067            self,
1068            move |obj, cancellable, send| {
1069                obj.make_symbolic_link_async(
1070                    &symlink_value,
1071                    io_priority,
1072                    Some(cancellable),
1073                    move |res| {
1074                        send.resolve(res);
1075                    },
1076                );
1077            },
1078        ))
1079    }
1080
1081    #[cfg(feature = "v2_72")]
1082    #[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
1083    fn move_future(
1084        &self,
1085        destination: &(impl IsA<File> + Clone + 'static),
1086        flags: crate::FileCopyFlags,
1087        io_priority: glib::Priority,
1088    ) -> (
1089        Pin<Box<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>>,
1090        Pin<Box<dyn futures_core::stream::Stream<Item = (i64, i64)> + 'static>>,
1091    ) {
1092        let destination = destination.clone();
1093
1094        let (sender, receiver) = futures_channel::mpsc::unbounded();
1095
1096        let fut = Box::pin(crate::GioFuture::new(
1097            self,
1098            move |obj, cancellable, send| {
1099                obj.move_async(
1100                    &destination,
1101                    flags,
1102                    io_priority,
1103                    Some(cancellable),
1104                    Some(Box::new(move |current_num_bytes, total_num_bytes| {
1105                        let _ = sender.unbounded_send((current_num_bytes, total_num_bytes));
1106                    })),
1107                    move |res| {
1108                        send.resolve(res);
1109                    },
1110                );
1111            },
1112        ));
1113
1114        (fut, Box::pin(receiver))
1115    }
1116
1117    #[doc(alias = "g_file_set_attribute")]
1118    fn set_attribute<'a>(
1119        &self,
1120        attribute: &str,
1121        value: impl Into<FileAttributeValue<'a>>,
1122        flags: FileQueryInfoFlags,
1123        cancellable: Option<&impl IsA<Cancellable>>,
1124    ) -> Result<(), glib::Error> {
1125        unsafe {
1126            let mut error = std::ptr::null_mut();
1127            let value: FileAttributeValue<'a> = value.into();
1128            let is_ok = ffi::g_file_set_attribute(
1129                self.as_ref().to_glib_none().0,
1130                attribute.to_glib_none().0,
1131                value.type_().into_glib(),
1132                value.as_ptr(),
1133                flags.into_glib(),
1134                cancellable.map(|p| p.as_ref()).to_glib_none().0,
1135                &mut error,
1136            );
1137            debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null());
1138            if error.is_null() {
1139                Ok(())
1140            } else {
1141                Err(from_glib_full(error))
1142            }
1143        }
1144    }
1145}
1146
1147impl<O: IsA<File>> FileExtManual for O {}