diff --git a/Cargo.lock b/Cargo.lock index e973f018ea6d..606b50d713c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,9 +122,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.18.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a2b34126159980f92da2a08bdec0694fd80fb5eb9e48aff25d20a0d8dfa710d" +checksum = "0d0390889d58f934f01cd49736275b4c2da15bcfc328c78ff2349907e6cabf22" dependencies = [ "smallvec", "target-lexicon", diff --git a/examples/Cargo.toml b/examples/Cargo.toml index c703b6bf524f..b1c990a3285d 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -51,3 +51,8 @@ path = "object_subclass/main.rs" [[bin]] name = "virtual_methods" path = "virtual_methods/main.rs" + +[lib] +name = "gio_vfs" +path = "gio_vfs/lib.rs" +crate-type = ["cdylib"] diff --git a/examples/gio_vfs/README.md b/examples/gio_vfs/README.md new file mode 100644 index 000000000000..1b0f2da24ac0 --- /dev/null +++ b/examples/gio_vfs/README.md @@ -0,0 +1,54 @@ +# GIO VFS + +This example demonstrates the usage of GIO VFS. Built artifact is a dynamic system library that is used as a GIO module +(see https://docs.gtk.org/gio/overview.html#running-gio-applications) +and that implement support of file operations for files with uri starting with `myvfs:///` + +Build, install and configure it by executing: +```bash +cargo build -p gtk-rs-examples --lib +export GIO_EXTRA_MODULES=/tmp/gio_modules +mkdir -p $GIO_EXTRA_MODULES && cp ./target/debug/libgio_vfs.so $GIO_EXTRA_MODULES +export MYVFS_ROOT=/tmp/myvfs +mkdir -p $MYVFS_ROOT +``` + +`GIO_EXTRA_MODULES` specify additional directories for `gio` command line tool to automatically load modules. + +`MYVFS_ROOT` specify the local directory that is used by as backend directory for uri starting with `myvfs:///` (e.g. if `MYVFS_ROOT-/tmp` `myvfs:///foo` points to `/tmp/foo`). + +`gio` commandline tool (see https://gnome.pages.gitlab.gnome.org/libsoup/gio/gio.html) automatically loads this extra module. + +Run it by executing the following commands: + +Basic operations: +```bash +echo "foo" | gio save myvfs:///foo +gio cat myvfs:///foo +gio set -t string myvfs:///foo xattr::my_string value +gio info myvfs:///foo +gio mkdir myvfs:///bar +gio copy myvfs:///foo myvfs:///bar/ +gio list myvfs:/// +gio tree myvfs:/// +gio move -b myvfs:///bar/foo myvfs:///foo +gio tree myvfs:/// +gio remove myvfs:///foo myvfs:///foo~ myvfs:///bar +gio list myvfs:/// +``` + +Monitor `myvfs:///`: +```bash +# monitor is a blocking operation. kill it with Ctrl+C +gio monitor myvfs:/// +``` + +```bash +# in another terminal (ensure MYVFS_ROOT is defined) +touch $MYVFS_ROOT/foo +echo "foo" > $MYVFS_ROOT/foo +mkdir $MYVFS_ROOT/bar +cp $MYVFS_ROOT/foo $MYVFS_ROOT/foo2 +mv -b $MYVFS_ROOT/foo2 $MYVFS_ROOT/foo +rm -rf $MYVFS_ROOT/* +``` diff --git a/examples/gio_vfs/file.rs b/examples/gio_vfs/file.rs new file mode 100644 index 000000000000..fa165b94c14e --- /dev/null +++ b/examples/gio_vfs/file.rs @@ -0,0 +1,533 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use std::path::PathBuf; + +use gio::prelude::*; +use gio::subclass::prelude::*; +use gio::File; +use gio::Vfs; +use glib::g_debug; +use glib::Object; + +// Define `MyFile` as an implementation of `File`. +pub mod imp { + use std::{path::PathBuf, sync::OnceLock}; + + use gio::{ + Cancellable, FileAttributeInfoList, FileAttributeValue, FileCopyFlags, FileCreateFlags, + FileEnumerator, FileInfo, FileInputStream, FileMonitor, FileMonitorFlags, FileOutputStream, + FileQueryInfoFlags, IOErrorEnum, + }; + use glib::{translate::ToGlibPtr, Error, Properties}; + + use crate::{ + file_enumerator::MyFileEnumerator, + file_monitor::MyFileMonitor, + {update_file_info, SCHEME}, + }; + + use super::*; + + #[derive(Properties, Default, Debug)] + #[properties(wrapper_type = super::MyFile)] + pub struct MyFile { + #[property(get, set)] + virtual_path: OnceLock, + #[property(get, set)] + local_file: OnceLock, + } + + impl MyFile { + fn virtual_path(&self) -> &PathBuf { + self.virtual_path.get().unwrap() + } + + fn local_file(&self) -> &File { + self.local_file.get().unwrap() + } + } + + #[glib::object_subclass] + #[object_subclass_dynamic(lazy_registration = true)] + impl ObjectSubclass for MyFile { + const NAME: &'static str = "MyFile"; + type Type = super::MyFile; + type Interfaces = (File,); + } + + #[glib::derived_properties] + impl ObjectImpl for MyFile {} + + impl FileImpl for MyFile { + fn dup(&self) -> File { + g_debug!("MyVfs", "MyFile::dup({:?})", self); + Self::Type::new(self.virtual_path().clone(), self.local_file().clone()).upcast() + } + + fn hash(&self) -> u32 { + g_debug!("MyVfs", "MyFile::hash({:?})", self); + unsafe { + gio::ffi::g_file_hash( + ToGlibPtr::<*const gio::ffi::GFile>::to_glib_none(self.local_file()).0 + as *const _, + ) + } + } + + fn equal(&self, file2: &File) -> bool { + g_debug!("MyVfs", "MyFile::equal({:?},{:?})", self, file2); + match file2.downcast_ref::() { + Some(file2) => self.local_file().equal(file2), + None => false, + } + } + + fn is_native(&self) -> bool { + g_debug!("MyVfs", "MyFile::is_native({:?})", self); + false + } + + fn has_uri_scheme(&self, uri_scheme: &str) -> bool { + g_debug!("MyVfs", "MyFile::has_uri_scheme({:?},{})", self, uri_scheme); + uri_scheme == SCHEME + } + + fn uri_scheme(&self) -> Option { + g_debug!("MyVfs", "MyFile::uri_scheme({:?})", self); + Some(SCHEME.to_owned()) + } + + fn basename(&self) -> Option { + g_debug!("MyVfs", "MyFile::basename({:?})", self); + self.local_file().basename() + } + + fn path(&self) -> Option { + g_debug!("MyVfs", "MyFile::path({:?})", self); + self.local_file().path() + } + + fn uri(&self) -> String { + g_debug!("MyVfs", "MyFile::uri({:?})", self); + format!( + "{}://{}", + SCHEME, + self.local_file().path().unwrap().to_string_lossy() + ) + } + + fn parse_name(&self) -> String { + g_debug!("MyVfs", "MyFile::parse_name({:?})", self); + self.uri() + } + + fn parent(&self) -> Option { + g_debug!("MyVfs", "MyFile::parent({:?})", self); + match (self.virtual_path().parent(), self.local_file().parent()) { + (Some(virtual_path), Some(local_file)) => { + Some(Self::Type::new(virtual_path.to_path_buf(), local_file).upcast()) + } + _ => None, + } + } + + fn has_prefix(&self, prefix: &File) -> bool { + g_debug!("MyVfs", "MyFile::has_prefix({:?},{:?})", self, prefix); + self.local_file().has_prefix(prefix) + } + + fn relative_path(&self, descendant: &File) -> Option { + g_debug!( + "MyVfs", + "MyFile::relative_path({:?},{:?})", + self, + descendant + ); + match descendant.downcast_ref::() { + Some(descendant) => descendant + .virtual_path() + .strip_prefix(self.virtual_path()) + .ok() + .map(PathBuf::from), + None => None, + } + } + + fn resolve_relative_path(&self, relative_path: impl AsRef) -> File { + g_debug!( + "MyVfs", + "MyFile::resolve_relative_path({:?},{:?})", + self, + relative_path.as_ref() + ); + let relative_path_as_pb = PathBuf::from(relative_path.as_ref()); + let (virtual_path, local_file) = if relative_path_as_pb.is_absolute() { + ( + relative_path_as_pb, + Vfs::local().file_for_path(relative_path), + ) + } else { + ( + self.virtual_path().join(relative_path_as_pb), + self.local_file().resolve_relative_path(relative_path), + ) + }; + Self::Type::new(virtual_path, local_file).upcast() + } + + fn child_for_display_name(&self, display_name: &str) -> Result { + g_debug!( + "MyVfs", + "MyFile::child_for_display_name({:?},{})", + self, + display_name + ); + let virtual_path = self.virtual_path().join(display_name); + let local_file = self.local_file().child_for_display_name(display_name)?; + Ok(Self::Type::new(virtual_path, local_file).upcast()) + } + + fn enumerate_children( + &self, + attributes: &str, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFile::enumerate_children({:?},{},{:?},{:?})", + self, + attributes, + flags, + cancellable.map(|_| "_") + ); + MyFileEnumerator::new(self.local_file(), attributes, flags, cancellable) + .map(|my_file_enumerator| my_file_enumerator.upcast()) + } + + fn query_info( + &self, + attributes: &str, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFile::query_info({:?},{},{:?},{:?})", + self, + attributes, + flags, + cancellable.map(|_| "_") + ); + let info = self + .local_file() + .query_info(attributes, flags, cancellable)?; + update_file_info(&info); + Ok(info) + } + + fn query_filesystem_info( + &self, + attributes: &str, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFile::query_filesystem_info({:?},{},{:?})", + self, + attributes, + cancellable.map(|_| "_") + ); + let info = self + .local_file() + .query_filesystem_info(attributes, cancellable)?; + update_file_info(&info); + Ok(info) + } + + fn set_display_name( + &self, + display_name: &str, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFile::set_display_name({:?},{},{:?})", + self, + display_name, + cancellable.map(|_| "_") + ); + let mut virtual_path = self.virtual_path().clone(); + let local_file = self + .local_file() + .set_display_name(display_name, cancellable)?; + let basename = local_file.basename().ok_or(Error::new( + IOErrorEnum::InvalidFilename, + &format!( + "failed to rename {} to {}", + virtual_path.file_name().unwrap().to_string_lossy(), + display_name + ), + ))?; + virtual_path.set_file_name(basename); + Ok(Self::Type::new(virtual_path, local_file).upcast()) + } + + fn query_settable_attributes( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFile::query_settable_attributes({:?},{:?})", + self, + cancellable.map(|_| "_") + ); + self.local_file().query_settable_attributes(cancellable) + } + + fn query_writable_namespaces( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFile::query_writable_namespaces({:?},{:?})", + self, + cancellable.map(|_| "_") + ); + self.local_file().query_writable_namespaces(cancellable) + } + + fn set_attribute<'a>( + &self, + attribute: &str, + value: impl Into>, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result<(), Error> { + let value: FileAttributeValue<'a> = value.into(); + g_debug!( + "MyVfs", + "MyFile::set_attribute({:?},{},{:?},{:?},{:?})", + self, + attribute, + value, + flags, + cancellable.map(|_| "_") + ); + self.local_file() + .set_attribute(attribute, value, flags, cancellable) + } + + fn read_fn( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFile::read_fn({:?},{:?})", + self, + cancellable.map(|_| "_") + ); + self.local_file().read(cancellable) + } + + fn append_to( + &self, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFile::append_to({:?},{:?},{:?})", + self, + flags, + cancellable.map(|_| "_") + ); + self.local_file().append_to(flags, cancellable) + } + + fn create( + &self, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFile::create({:?},{:?},{:?})", + self, + flags, + cancellable.map(|_| "_") + ); + self.local_file().create(flags, cancellable) + } + + fn replace( + &self, + etag: Option<&str>, + make_backup: bool, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFile::replace({:?},{:?},{},{:?},{:?})", + self, + etag, + make_backup, + flags, + cancellable.map(|_| "_") + ); + self.local_file().replace( + etag.map(AsRef::::as_ref), + make_backup, + flags, + cancellable, + ) + } + + fn delete(&self, cancellable: Option<&impl IsA>) -> Result<(), Error> { + g_debug!( + "MyVfs", + "MyFile::delete({:?},{:?})", + self, + cancellable.map(|_| "_") + ); + self.local_file().delete(cancellable) + } + + fn trash(&self, cancellable: Option<&impl IsA>) -> Result<(), Error> { + g_debug!( + "MyVfs", + "MyFile::trash({:?},{:?})", + self, + cancellable.map(|_| "_") + ); + self.local_file().trash(cancellable) + } + + fn make_directory(&self, cancellable: Option<&impl IsA>) -> Result<(), Error> { + g_debug!( + "MyVfs", + "MyFile::make_directory({:?},{:?})", + self, + cancellable.map(|_| "_") + ); + self.local_file().make_directory(cancellable) + } + + fn copy( + source: &File, + destination: &File, + flags: FileCopyFlags, + cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(), Error> { + g_debug!( + "MyVfs", + "MyFile::copy({:?},{:?},{:?},{:?},{:?})", + source, + destination, + flags, + cancellable.map(|_| "_"), + progress_callback.as_ref().map(|_| "_") + ); + let source = source + .downcast_ref::() + .map(|my_file| my_file.imp().local_file()) + .unwrap_or(source); + let destination = destination + .downcast_ref::() + .map(|my_file| my_file.imp().local_file()) + .unwrap_or(destination); + source.copy(destination, flags, cancellable, progress_callback) + } + + fn move_( + source: &File, + destination: &File, + flags: FileCopyFlags, + cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(), Error> { + g_debug!( + "MyVfs", + "MyFile::move_({:?},{:?},{:?},{:?},{:?})", + source, + destination, + flags, + cancellable.map(|_| "_"), + progress_callback.as_ref().map(|_| "_") + ); + let source = source + .downcast_ref::() + .map(|my_file| my_file.imp().local_file()) + .unwrap_or(source); + let destination = destination + .downcast_ref::() + .map(|my_file| my_file.imp().local_file()) + .unwrap_or(destination); + source.move_(destination, flags, cancellable, progress_callback) + } + + fn monitor_dir( + &self, + flags: FileMonitorFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFile::monitor_dir({:?},{:?},{:?})", + self, + flags, + cancellable.map(|_| "_") + ); + MyFileMonitor::for_directory(self.local_file(), flags, cancellable) + .map(|my_file_monitor| my_file_monitor.upcast()) + } + + fn monitor_file( + &self, + flags: FileMonitorFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFile::monitor_file({:?},{:?},{:?})", + self, + flags, + cancellable.map(|_| "_") + ); + MyFileMonitor::for_file(self.local_file(), flags, cancellable) + .map(|my_file_monitor| my_file_monitor.upcast()) + } + } +} + +glib::wrapper! { + pub struct MyFile(ObjectSubclass) @implements File; +} + +impl MyFile { + pub fn new(virtual_path: PathBuf, local_file: File) -> Self { + g_debug!("MyVfs", "MyFile::new({:?},{:?})", virtual_path, local_file); + Object::builder() + .property("virtual_path", virtual_path) + .property("local_file", local_file) + .build() + } +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use super::*; + + #[test] + fn test() { + let f = MyFile::new(PathBuf::from("/"), Vfs::local().file_for_path("/")); + assert_eq!(f.path(), Some(PathBuf::from("/"))); + } +} diff --git a/examples/gio_vfs/file_enumerator.rs b/examples/gio_vfs/file_enumerator.rs new file mode 100644 index 000000000000..a10b95a5099f --- /dev/null +++ b/examples/gio_vfs/file_enumerator.rs @@ -0,0 +1,94 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use gio::prelude::*; +use gio::subclass::prelude::*; +use gio::Cancellable; +use gio::File; +use gio::FileEnumerator; +use gio::FileQueryInfoFlags; +use glib::g_debug; +use glib::Error; +use glib::Object; + +// Define `MyFileEnumerator` as an implementation of `FileEnumerator`. +pub mod imp { + use std::{path::PathBuf, sync::OnceLock}; + + use glib::Properties; + + use crate::update_file_info; + + use super::*; + + // #[derive(Default)] + #[derive(Properties, Default)] + #[properties(wrapper_type = super::MyFileEnumerator)] + pub struct MyFileEnumerator { + #[property(get, set)] + virtual_path: OnceLock, + #[property(get, set)] + local_file_enumerator: OnceLock, + } + + impl MyFileEnumerator { + fn local_file_enumerator(&self) -> &FileEnumerator { + self.local_file_enumerator.get().unwrap() + } + } + + #[glib::object_subclass] + #[object_subclass_dynamic(lazy_registration = true)] + impl ObjectSubclass for MyFileEnumerator { + const NAME: &'static str = "MyFileEnumerator"; + type Type = super::MyFileEnumerator; + type ParentType = FileEnumerator; + } + + #[glib::derived_properties] + impl ObjectImpl for MyFileEnumerator {} + + impl FileEnumeratorImpl for MyFileEnumerator { + fn next_file( + &self, + cancellable: Option<&gio::Cancellable>, + ) -> Result, glib::Error> { + if let Some(info) = self.local_file_enumerator().next_file(cancellable)? { + update_file_info(&info); + Ok(Some(info)) + } else { + Ok(None) + } + } + + fn close(&self, cancellable: Option<&gio::Cancellable>) -> (bool, Option) { + self.local_file_enumerator().close(cancellable) + } + } +} + +glib::wrapper! { + pub struct MyFileEnumerator(ObjectSubclass) @extends FileEnumerator; +} + +impl MyFileEnumerator { + pub fn new( + local_file: &File, + attributes: &str, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFileEnumerator::new({:?},{},{:?},{:?})", + local_file, + attributes, + flags, + cancellable.map(|_| "_") + ); + let local_file_enumerator = + local_file.enumerate_children(attributes, flags, cancellable)?; + Ok(Object::builder() + .property("local_file_enumerator", local_file_enumerator) + .build()) + } +} diff --git a/examples/gio_vfs/file_monitor.rs b/examples/gio_vfs/file_monitor.rs new file mode 100644 index 000000000000..5850242d2640 --- /dev/null +++ b/examples/gio_vfs/file_monitor.rs @@ -0,0 +1,121 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use gio::prelude::*; +use gio::subclass::prelude::*; +use gio::Cancellable; +use gio::File; +use gio::FileMonitor; +use gio::FileMonitorFlags; +use glib::g_debug; +use glib::Error; +use glib::Object; + +use crate::file::MyFile; +use crate::resolve_local_path; + +// Define `MyFileMonitor` as an implementation of `FileMonitor`. +pub mod imp { + use std::{path::PathBuf, sync::OnceLock}; + + use glib::Properties; + + use super::*; + + // #[derive(Default)] + #[derive(Properties, Default)] + #[properties(wrapper_type = super::MyFileMonitor)] + pub struct MyFileMonitor { + #[property(get, set)] + virtual_path: OnceLock, + #[property(get, set)] + local_file_monitor: OnceLock, + } + + impl MyFileMonitor { + pub(super) fn local_file_monitor(&self) -> &FileMonitor { + self.local_file_monitor.get().unwrap() + } + } + + #[glib::object_subclass] + #[object_subclass_dynamic(lazy_registration = true)] + impl ObjectSubclass for MyFileMonitor { + const NAME: &'static str = "MyFileMonitor"; + type Type = super::MyFileMonitor; + type ParentType = FileMonitor; + } + + #[glib::derived_properties] + impl ObjectImpl for MyFileMonitor {} + + impl FileMonitorImpl for MyFileMonitor { + fn cancel(&self) { + self.local_file_monitor().cancel(); + } + } +} + +glib::wrapper! { + pub struct MyFileMonitor(ObjectSubclass) @extends FileMonitor; +} + +impl MyFileMonitor { + pub fn for_directory( + local_file: &File, + flags: FileMonitorFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFileMonitor::for_directory({:?},{:?},{:?})", + local_file, + flags, + cancellable.map(|_| "_") + ); + let local_file_monitor = local_file.monitor_directory(flags, cancellable)?; + Self::new(local_file_monitor) + } + + pub fn for_file( + local_file: &File, + flags: FileMonitorFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + g_debug!( + "MyVfs", + "MyFileMonitor::for_file({:?},{:?},{:?})", + local_file, + flags, + cancellable.map(|_| "_") + ); + let local_file_monitor = local_file.monitor_file(flags, cancellable)?; + Self::new(local_file_monitor) + } + + pub fn new(local_file_monitor: FileMonitor) -> Result { + let file_monitor = Object::builder::() + .property("local_file_monitor", local_file_monitor) + .build(); + file_monitor + .imp() + .local_file_monitor() + .connect_changed(glib::clone!( + #[weak] + file_monitor, + move |_, local_file, other_local_file, event_type| { + let file = MyFile::new( + resolve_local_path(local_file.path().unwrap().to_string_lossy()).into(), + local_file.clone(), + ); + let other_file = other_local_file.map(|local_file| { + MyFile::new( + resolve_local_path(local_file.path().unwrap().to_string_lossy()).into(), + local_file.clone(), + ) + }); + file_monitor.emit_event(&file, other_file.as_ref(), event_type); + } + )); + Ok(file_monitor) + } +} diff --git a/examples/gio_vfs/lib.rs b/examples/gio_vfs/lib.rs new file mode 100644 index 000000000000..2176c2435346 --- /dev/null +++ b/examples/gio_vfs/lib.rs @@ -0,0 +1,144 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use gio::{IOErrorEnum, IOExtensionPoint, VFS_EXTENSION_POINT_NAME}; +use glib::{g_debug, translate::FromGlibPtrBorrow, types::StaticType}; + +mod file; +mod file_enumerator; +mod file_monitor; +mod vfs; + +#[no_mangle] +pub extern "C" fn g_io_module_load(module_ptr: *mut gio::ffi::GIOModule) { + let type_module = unsafe { glib::TypeModule::from_glib_borrow(module_ptr as *mut _) }; + let res = register(&type_module); + assert!(res.is_ok(), "{}", res.err().unwrap()); +} + +#[no_mangle] +pub extern "C" fn g_io_module_unload(module_ptr: *mut gio::ffi::GIOModule) { + let type_module = unsafe { glib::TypeModule::from_glib_borrow(module_ptr as *mut _) }; + let res = unregister(&type_module); + debug_assert!(res.is_ok(), "{}", res.err().unwrap()); +} + +pub fn register(type_module: &glib::TypeModule) -> Result<(), glib::Error> { + // register module types + if !vfs::imp::MyVfs::on_implementation_load(type_module) { + Err(glib::Error::new( + IOErrorEnum::Failed, + "failed to register module type MyVfs", + ))?; + } + if !file::imp::MyFile::on_implementation_load(type_module) { + Err(glib::Error::new( + IOErrorEnum::Failed, + "failed to register module type MyFile", + ))?; + } + if !file_enumerator::imp::MyFileEnumerator::on_implementation_load(type_module) { + Err(glib::Error::new( + IOErrorEnum::Failed, + "failed to register module type MyFileEnumerator", + ))?; + } + if !file_monitor::imp::MyFileMonitor::on_implementation_load(type_module) { + Err(glib::Error::new( + IOErrorEnum::Failed, + "failed to register module type MyFileMonitor", + ))?; + } + + if IOExtensionPoint::lookup(VFS_EXTENSION_POINT_NAME).is_none() { + let _ = IOExtensionPoint::builder(VFS_EXTENSION_POINT_NAME).build(); + } + IOExtensionPoint::implement( + VFS_EXTENSION_POINT_NAME, + vfs::MyVfs::static_type(), + SCHEME, + 20, + ) + .ok_or(glib::Error::new( + IOErrorEnum::Failed, + "failed to register vfs extension point", + ))?; + + g_debug!("MyVfs", "myvfs registered !!!"); + Ok(()) +} + +pub fn unregister(type_module: &glib::TypeModule) -> Result<(), glib::Error> { + // unregister module types + if !file_monitor::imp::MyFileMonitor::on_implementation_unload(type_module) { + Err(glib::Error::new( + IOErrorEnum::Failed, + "failed to unregister module type MyFileMonitor", + ))?; + } + if !file_enumerator::imp::MyFileEnumerator::on_implementation_unload(type_module) { + Err(glib::Error::new( + IOErrorEnum::Failed, + "failed to register module type MyFileEnumerator", + ))?; + } + if !file::imp::MyFile::on_implementation_unload(type_module) { + Err(glib::Error::new( + IOErrorEnum::Failed, + "failed to register module type MyFile", + ))?; + } + if !vfs::imp::MyVfs::on_implementation_unload(type_module) { + Err(glib::Error::new( + IOErrorEnum::Failed, + "failed to register module type MyVfs", + ))?; + } + + g_debug!("MyVfs", "myvfs unregistered !!!"); + Ok(()) +} + +pub const SCHEME: &str = "myvfs"; + +pub const MYVFS_ROOT: &str = "MYVFS_ROOT"; + +pub const DEFAULT_MYVFS_ROOT: &str = "/tmp/myvfs"; + +pub fn resolve_virtual_path>(local_path: T) -> String { + let local_root = glib::getenv(MYVFS_ROOT) + .and_then(|os| os.into_string().ok()) + .unwrap_or(DEFAULT_MYVFS_ROOT.to_string()); + g_debug!( + "MyVfs", + "resolve_virtual_path({},{})", + local_root, + local_path.as_ref() + ); + local_path + .as_ref() + .strip_prefix(&local_root) + .unwrap_or(local_path.as_ref()) + .to_string() +} + +pub fn resolve_local_path>(virtual_path: T) -> String { + let local_root = glib::getenv(MYVFS_ROOT) + .and_then(|os| os.into_string().ok()) + .unwrap_or(DEFAULT_MYVFS_ROOT.to_string()); + g_debug!( + "MyVfs", + "resolve_local_path({},{})", + local_root, + virtual_path.as_ref() + ); + format!("{}/{}", local_root, virtual_path.as_ref()).replace("//", "/") +} + +pub fn update_file_info(info: &gio::FileInfo) { + if let Some(v) = info.attribute_as_string(gio::FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET) { + info.set_attribute_string( + gio::FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, + &format!("{}://{}", SCHEME, resolve_virtual_path(v)), + ); + } +} diff --git a/examples/gio_vfs/vfs.rs b/examples/gio_vfs/vfs.rs new file mode 100644 index 000000000000..36faf2c97871 --- /dev/null +++ b/examples/gio_vfs/vfs.rs @@ -0,0 +1,85 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use gio::{prelude::*, subclass::prelude::*, File, Vfs}; +use glib::g_debug; + +use crate::SCHEME; + +// Define `MyVfs` as a subclass of `Vfs`. +pub mod imp { + use std::{path::PathBuf, sync::LazyLock}; + + use glib::{object::Cast, StrVRef}; + + use crate::{file::MyFile, resolve_local_path}; + + use super::*; + + #[derive(Default, Debug)] + pub struct MyVfs; + + #[glib::object_subclass] + #[object_subclass_dynamic(lazy_registration = true)] + impl ObjectSubclass for MyVfs { + const NAME: &'static str = "MyVfs"; + type Type = super::MyVfs; + type ParentType = Vfs; + } + + impl ObjectImpl for MyVfs {} + + impl VfsImpl for MyVfs { + fn is_active(&self) -> bool { + true + } + + fn get_file_for_path(&self, path: &std::path::Path) -> File { + g_debug!("MyVfs", "MyVfs::get_file_for_path({:?},{:?})", self, path); + Vfs::local().file_for_path(path) + } + + fn get_file_for_uri(&self, uri: &str) -> File { + g_debug!("MyVfs", "MyVfs::get_file_for_uri({:?},{})", self, uri); + if let Some(path) = uri.strip_prefix(&format!("{SCHEME}://")) { + MyFile::new( + PathBuf::from(path), + Vfs::local().file_for_path(resolve_local_path(path)), + ) + .upcast() + } else { + Vfs::local().file_for_uri(uri) + } + } + + fn get_supported_uri_schemes(&self) -> &'static StrVRef { + g_debug!("MyVfs", "MyVfs::get_supported_uri_schemes({:?})", self); + static SUPPORTED_URI_SCHEMES: LazyLock = LazyLock::new(|| { + let mut schemes: Vec = Vfs::local() + .supported_uri_schemes() + .iter() + .map(|scheme| scheme.to_string()) + .collect(); + schemes.push(SCHEME.to_owned()); + glib::StrV::from(schemes) + }); + &SUPPORTED_URI_SCHEMES + } + + fn parse_name(&self, parse_name: &str) -> File { + g_debug!("MyVfs", "MyVfs::parse_name({:?},{})", self, parse_name); + if let Some(path) = parse_name.strip_prefix(&format!("{SCHEME}://")) { + MyFile::new( + PathBuf::from(path), + Vfs::local().parse_name(&resolve_local_path(path)), + ) + .upcast() + } else { + Vfs::local().parse_name(parse_name) + } + } + } +} + +glib::wrapper! { + pub struct MyVfs(ObjectSubclass) @extends Vfs; +} diff --git a/gio/Gir.toml b/gio/Gir.toml index 616b028ea77f..43b9f519d188 100644 --- a/gio/Gir.toml +++ b/gio/Gir.toml @@ -796,6 +796,10 @@ manual_traits = ["FileExtManual"] manual = true doc_trait_name = "FileExtManual" [[object.function]] + name = "copy" + # FnMut callback + manual = true + [[object.function]] name = "copy_async" # Multiple callbacks manual = true @@ -805,6 +809,10 @@ manual_traits = ["FileExtManual"] # Manually implemented above ignore = true [[object.function]] + name = "move" + # FnMut callback + manual = true + [[object.function]] name = "move_async" # Multiple callbacks manual = true diff --git a/gio/src/auto/file.rs b/gio/src/auto/file.rs index 6e8337f6323a..091a92010c96 100644 --- a/gio/src/auto/file.rs +++ b/gio/src/auto/file.rs @@ -216,53 +216,6 @@ pub trait FileExt: IsA + 'static { } } - #[doc(alias = "g_file_copy")] - fn copy( - &self, - destination: &impl IsA, - flags: FileCopyFlags, - cancellable: Option<&impl IsA>, - progress_callback: Option<&mut dyn FnMut(i64, i64)>, - ) -> Result<(), glib::Error> { - let mut progress_callback_data: Option<&mut dyn FnMut(i64, i64)> = progress_callback; - unsafe extern "C" fn progress_callback_func( - current_num_bytes: i64, - total_num_bytes: i64, - data: glib::ffi::gpointer, - ) { - let callback = data as *mut Option<&mut dyn FnMut(i64, i64)>; - if let Some(ref mut callback) = *callback { - callback(current_num_bytes, total_num_bytes) - } else { - panic!("cannot get closure...") - } - } - let progress_callback = if progress_callback_data.is_some() { - Some(progress_callback_func as _) - } else { - None - }; - let super_callback0: &mut Option<&mut dyn FnMut(i64, i64)> = &mut progress_callback_data; - unsafe { - let mut error = std::ptr::null_mut(); - let is_ok = ffi::g_file_copy( - self.as_ref().to_glib_none().0, - destination.as_ref().to_glib_none().0, - flags.into_glib(), - cancellable.map(|p| p.as_ref()).to_glib_none().0, - progress_callback, - super_callback0 as *mut _ as *mut _, - &mut error, - ); - debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); - if error.is_null() { - Ok(()) - } else { - Err(from_glib_full(error)) - } - } - } - #[doc(alias = "g_file_copy_attributes")] fn copy_attributes( &self, @@ -1239,54 +1192,6 @@ pub trait FileExt: IsA + 'static { )) } - #[doc(alias = "g_file_move")] - #[doc(alias = "move")] - fn move_( - &self, - destination: &impl IsA, - flags: FileCopyFlags, - cancellable: Option<&impl IsA>, - progress_callback: Option<&mut dyn FnMut(i64, i64)>, - ) -> Result<(), glib::Error> { - let mut progress_callback_data: Option<&mut dyn FnMut(i64, i64)> = progress_callback; - unsafe extern "C" fn progress_callback_func( - current_num_bytes: i64, - total_num_bytes: i64, - data: glib::ffi::gpointer, - ) { - let callback = data as *mut Option<&mut dyn FnMut(i64, i64)>; - if let Some(ref mut callback) = *callback { - callback(current_num_bytes, total_num_bytes) - } else { - panic!("cannot get closure...") - } - } - let progress_callback = if progress_callback_data.is_some() { - Some(progress_callback_func as _) - } else { - None - }; - let super_callback0: &mut Option<&mut dyn FnMut(i64, i64)> = &mut progress_callback_data; - unsafe { - let mut error = std::ptr::null_mut(); - let is_ok = ffi::g_file_move( - self.as_ref().to_glib_none().0, - destination.as_ref().to_glib_none().0, - flags.into_glib(), - cancellable.map(|p| p.as_ref()).to_glib_none().0, - progress_callback, - super_callback0 as *mut _ as *mut _, - &mut error, - ); - debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); - if error.is_null() { - Ok(()) - } else { - Err(from_glib_full(error)) - } - } - } - #[doc(alias = "g_file_open_readwrite")] fn open_readwrite( &self, diff --git a/gio/src/file.rs b/gio/src/file.rs index 52ab7b94bd42..2242c47823cc 100644 --- a/gio/src/file.rs +++ b/gio/src/file.rs @@ -7,7 +7,8 @@ use glib::{prelude::*, translate::*}; #[cfg(feature = "v2_74")] use crate::FileIOStream; use crate::{ - ffi, Cancellable, File, FileAttributeValue, FileCreateFlags, FileEnumerator, FileQueryInfoFlags, + ffi, Cancellable, File, FileAttributeValue, FileCopyFlags, FileCreateFlags, FileEnumerator, + FileQueryInfoFlags, }; impl File { @@ -357,6 +358,53 @@ pub trait FileExtManual: IsA + Sized { )) } + #[doc(alias = "g_file_copy")] + fn copy( + &self, + destination: &impl IsA, + flags: FileCopyFlags, + cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(), glib::Error> { + let mut super_callback0 = progress_callback; + let (progress_callback, progress_callback_data) = + super_callback0 + .as_mut() + .map_or((None, std::ptr::null_mut()), |progress_callback| { + unsafe extern "C" fn progress_callback_trampoline( + current_num_bytes: i64, + total_num_bytes: i64, + user_data: glib::ffi::gpointer, + ) { + let progress_callback: &mut Box = + &mut *(user_data as *mut _); + progress_callback(current_num_bytes, total_num_bytes); + } + ( + Some(progress_callback_trampoline as _), + progress_callback as *mut Box as *mut _, + ) + }); + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::g_file_copy( + self.as_ref().to_glib_none().0, + destination.as_ref().to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + progress_callback, + progress_callback_data, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + #[doc(alias = "g_file_copy_async")] fn copy_async) + 'static>( &self, @@ -704,32 +752,28 @@ pub trait FileExtManual: IsA + Sized { cancellable: Option<&impl IsA>, progress_callback: Option>, ) -> Result<(u64, u64, u64), glib::Error> { - let progress_callback_data: Box< - Option>>, - > = Box::new(progress_callback.map(RefCell::new)); - unsafe extern "C" fn progress_callback_func( + let mut super_callback0 = progress_callback; + unsafe extern "C" fn progress_callback_trampoline( reporting: glib::ffi::gboolean, current_size: u64, num_dirs: u64, num_files: u64, user_data: glib::ffi::gpointer, ) { + let progress_callback: &mut Box = + &mut *(user_data as *mut _); let reporting = from_glib(reporting); - let callback: &Option>> = - &*(user_data as *mut _); - if let Some(ref callback) = *callback { - (*callback.borrow_mut())(reporting, current_size, num_dirs, num_files) - } else { - panic!("cannot get closure...") - }; + progress_callback(reporting, current_size, num_dirs, num_files); } - let progress_callback = if progress_callback_data.is_some() { - Some(progress_callback_func as _) + let progress_callback = if super_callback0.is_some() { + Some(progress_callback_trampoline as _) } else { None }; - let super_callback0: Box>>> = - progress_callback_data; + let progress_callback_data = super_callback0 + .as_mut() + .map_or(std::ptr::null_mut(), |data| data as *mut _) + as *mut _; unsafe { let mut disk_usage = mem::MaybeUninit::uninit(); let mut num_dirs = mem::MaybeUninit::uninit(); @@ -740,7 +784,7 @@ pub trait FileExtManual: IsA + Sized { flags.into_glib(), cancellable.map(|p| p.as_ref()).to_glib_none().0, progress_callback, - Box::into_raw(super_callback0) as *mut _, + progress_callback_data, disk_usage.as_mut_ptr(), num_dirs.as_mut_ptr(), num_files.as_mut_ptr(), @@ -910,6 +954,54 @@ pub trait FileExtManual: IsA + Sized { (fut, Box::pin(receiver)) } + #[doc(alias = "g_file_move")] + #[doc(alias = "move")] + fn move_( + &self, + destination: &impl IsA, + flags: FileCopyFlags, + cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(), glib::Error> { + let mut super_callback0 = progress_callback; + let (progress_callback, progress_callback_data) = + super_callback0 + .as_mut() + .map_or((None, std::ptr::null_mut()), |progress_callback| { + unsafe extern "C" fn progress_callback_trampoline( + current_num_bytes: i64, + total_num_bytes: i64, + user_data: glib::ffi::gpointer, + ) { + let progress_callback: &mut Box = + &mut *(user_data as *mut _); + progress_callback(current_num_bytes, total_num_bytes); + } + ( + Some(progress_callback_trampoline as _), + progress_callback as *mut Box as *mut _, + ) + }); + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::g_file_move( + self.as_ref().to_glib_none().0, + destination.as_ref().to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + progress_callback, + progress_callback_data, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + #[cfg(feature = "v2_72")] #[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))] #[doc(alias = "g_file_move_async")] diff --git a/gio/src/file_attribute_value.rs b/gio/src/file_attribute_value.rs index d87ed2678cdb..b2f9cb8e9221 100644 --- a/gio/src/file_attribute_value.rs +++ b/gio/src/file_attribute_value.rs @@ -76,11 +76,14 @@ impl FileAttributeValue<'_> { pub(crate) fn as_ptr(&self) -> glib::ffi::gpointer { self.0.as_ptr() } + + pub(crate) fn for_pointer(type_: FileAttributeType, value_p: *mut std::ffi::c_void) -> Self { + Self(FileAttributeValueInner::Pointer(type_, value_p)) + } } #[derive(Debug)] pub(crate) enum FileAttributeValueInner<'a> { - #[allow(dead_code)] // TODO remove this allow attribute when Pointer will be used by this crate Pointer(FileAttributeType, glib::ffi::gpointer), String(<&'a str as ToGlibPtr<'a, *mut libc::c_char>>::Storage), ByteString(&'a CStr), diff --git a/gio/src/subclass/file.rs b/gio/src/subclass/file.rs new file mode 100644 index 000000000000..6a5f12219b75 --- /dev/null +++ b/gio/src/subclass/file.rs @@ -0,0 +1,3682 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::{ + prelude::*, subclass::prelude::*, thread_guard, translate::*, Error, GString, Interface, Object, +}; + +use crate::{ + ffi, AsyncResult, Cancellable, DriveStartFlags, File, FileAttributeInfoList, + FileAttributeValue, FileCopyFlags, FileCreateFlags, FileEnumerator, FileIOStream, FileInfo, + FileInputStream, FileMeasureFlags, FileMonitor, FileMonitorFlags, FileOutputStream, + FileQueryInfoFlags, IOErrorEnum, Mount, MountMountFlags, MountOperation, MountUnmountFlags, + Task, +}; + +use libc::{c_char, c_uint}; + +use std::{boxed::Box, path::PathBuf}; + +// Support custom implementation of virtual functions defined in `gio::ffi::GFileIface` except pairs `xxx_async/xxx_finish` for which GIO provides a default implementation. +pub trait FileImpl: ObjectImpl + ObjectSubclass> { + const SUPPORT_THREAD_CONTEXT: bool = true; + + fn dup(&self) -> File { + self.parent_dup() + } + + fn hash(&self) -> u32 { + self.parent_hash() + } + + fn equal(&self, file2: &File) -> bool { + self.parent_equal(file2) + } + + fn is_native(&self) -> bool { + self.parent_is_native() + } + + fn has_uri_scheme(&self, uri_scheme: &str) -> bool { + self.parent_has_uri_scheme(uri_scheme) + } + + fn uri_scheme(&self) -> Option { + self.parent_uri_scheme() + } + + fn basename(&self) -> Option { + self.parent_basename() + } + + fn path(&self) -> Option { + self.parent_path() + } + + fn uri(&self) -> String { + self.parent_uri() + } + + fn parse_name(&self) -> String { + self.parent_parse_name() + } + + fn parent(&self) -> Option { + self.parent_parent() + } + + fn has_prefix(&self, prefix: &File) -> bool { + self.parent_has_prefix(prefix) + } + + fn relative_path(&self, descendant: &File) -> Option { + self.parent_relative_path(descendant) + } + + fn resolve_relative_path(&self, relative_path: impl AsRef) -> File { + self.parent_resolve_relative_path(relative_path) + } + + fn child_for_display_name(&self, display_name: &str) -> Result { + self.parent_child_for_display_name(display_name) + } + + fn enumerate_children( + &self, + attributes: &str, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_enumerate_children(attributes, flags, cancellable) + } + + fn query_info( + &self, + attributes: &str, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_query_info(attributes, flags, cancellable) + } + + fn query_filesystem_info( + &self, + attributes: &str, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_query_filesystem_info(attributes, cancellable) + } + + fn find_enclosing_mount( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_find_enclosing_mount(cancellable) + } + + fn set_display_name( + &self, + display_name: &str, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_set_display_name(display_name, cancellable) + } + + fn query_settable_attributes( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_query_settable_attributes(cancellable) + } + + fn query_writable_namespaces( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_query_writable_namespaces(cancellable) + } + + fn set_attribute<'a>( + &self, + attribute: &str, + value: impl Into>, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result<(), Error> { + self.parent_set_attribute(attribute, value, flags, cancellable) + } + + fn set_attributes_from_info( + &self, + info: &FileInfo, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result<(), Error> { + self.parent_set_attributes_from_info(info, flags, cancellable) + } + + fn read_fn( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_read_fn(cancellable) + } + + fn append_to( + &self, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_append_to(flags, cancellable) + } + + fn create( + &self, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_create(flags, cancellable) + } + + fn replace( + &self, + etag: Option<&str>, + make_backup: bool, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_replace(etag, make_backup, flags, cancellable) + } + + fn delete(&self, cancellable: Option<&impl IsA>) -> Result<(), Error> { + self.parent_delete(cancellable) + } + + fn trash(&self, cancellable: Option<&impl IsA>) -> Result<(), Error> { + self.parent_trash(cancellable) + } + + fn make_directory(&self, cancellable: Option<&impl IsA>) -> Result<(), Error> { + self.parent_make_directory(cancellable) + } + + fn make_symbolic_link( + &self, + symlink_value: impl AsRef, + cancellable: Option<&impl IsA>, + ) -> Result<(), Error> { + self.parent_make_symbolic_link(symlink_value, cancellable) + } + + fn copy( + source: &File, + destination: &File, + flags: FileCopyFlags, + cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(), Error> { + Self::parent_copy(source, destination, flags, cancellable, progress_callback) + } + + fn move_( + source: &File, + destination: &File, + flags: FileCopyFlags, + cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(), Error> { + Self::parent_move(source, destination, flags, cancellable, progress_callback) + } + + fn mount_mountable( + &self, + flags: MountMountFlags, + mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + self.parent_mount_mountable(flags, mount_operation, cancellable, callback) + } + + fn mount_mountable_finish(&self, res: &AsyncResult) -> Result { + self.parent_mount_mountable_finish(res) + } + + fn unmount_mountable( + &self, + flags: MountUnmountFlags, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + self.parent_unmount_mountable(flags, cancellable, callback) + } + + fn unmount_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + self.parent_unmount_mountable_finish(res) + } + + fn eject_mountable( + &self, + flags: MountUnmountFlags, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + self.parent_eject_mountable(flags, cancellable, callback) + } + + fn eject_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + self.parent_eject_mountable_finish(res) + } + + fn mount_enclosing_volume( + &self, + flags: MountMountFlags, + mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + self.parent_mount_enclosing_volume(flags, mount_operation, cancellable, callback) + } + + fn mount_enclosing_volume_finish(&self, res: &AsyncResult) -> Result<(), Error> { + self.parent_mount_enclosing_volume_finish(res) + } + + fn monitor_dir( + &self, + flags: FileMonitorFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_monitor_dir(flags, cancellable) + } + + fn monitor_file( + &self, + flags: FileMonitorFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_monitor_file(flags, cancellable) + } + + fn open_readwrite( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_open_readwrite(cancellable) + } + + fn create_readwrite( + &self, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_create_readwrite(flags, cancellable) + } + + fn replace_readwrite( + &self, + etag: Option<&str>, + make_backup: bool, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + self.parent_replace_readwrite(etag, make_backup, flags, cancellable) + } + + fn start_mountable( + &self, + flags: DriveStartFlags, + mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + self.parent_start_mountable(flags, mount_operation, cancellable, callback) + } + + fn start_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + self.parent_start_mountable_finish(res) + } + + fn stop_mountable( + &self, + flags: MountUnmountFlags, + mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + self.parent_stop_mountable(flags, mount_operation, cancellable, callback) + } + + fn stop_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + self.parent_stop_mountable_finish(res) + } + + fn unmount_mountable_with_operation( + &self, + flags: MountUnmountFlags, + mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + self.parent_unmount_mountable_with_operation(flags, mount_operation, cancellable, callback) + } + + fn unmount_mountable_with_operation_finish(&self, res: &AsyncResult) -> Result<(), Error> { + self.parent_unmount_mountable_with_operation_finish(res) + } + + fn eject_mountable_with_operation( + &self, + flags: MountUnmountFlags, + mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + self.parent_eject_mountable_with_operation(flags, mount_operation, cancellable, callback) + } + + fn eject_mountable_with_operation_finish(&self, res: &AsyncResult) -> Result<(), Error> { + self.parent_eject_mountable_with_operation_finish(res) + } + + fn poll_mountable( + &self, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + self.parent_poll_mountable(cancellable, callback) + } + + fn poll_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + self.parent_poll_mountable_finish(res) + } + + fn measure_disk_usage( + &self, + flags: FileMeasureFlags, + cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(u64, u64, u64), Error> { + self.parent_measure_disk_usage(flags, cancellable, progress_callback) + } + + fn query_exists(&self, cancellable: Option<&impl IsA>) -> bool { + self.parent_query_exists(cancellable) + } +} + +// Support parent implementation of virtual functions defined in `gio::ffi::GFileIface` except pairs `xxx_async/xxx_finish` for which GIO provides a default implementation. +pub trait FileImplExt: FileImpl { + fn parent_dup(&self) -> File { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .dup + .expect("no parent \"dup\" implementation"); + let ret = func(self.obj().unsafe_cast_ref::().to_glib_none().0); + from_glib_full(ret) + } + } + + fn parent_hash(&self) -> u32 { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .hash + .expect("no parent \"hash\" implementation"); + let ret = func(self.obj().unsafe_cast_ref::().to_glib_none().0); + ret + } + } + + fn parent_equal(&self, file2: &File) -> bool { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .equal + .expect("no parent \"equal\" implementation"); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + file2.to_glib_none().0, + ); + from_glib(ret) + } + } + + fn parent_is_native(&self) -> bool { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .is_native + .expect("no parent \"is_native\" implementation"); + let ret = func(self.obj().unsafe_cast_ref::().to_glib_none().0); + from_glib(ret) + } + } + + fn parent_has_uri_scheme(&self, uri_scheme: &str) -> bool { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .has_uri_scheme + .expect("no parent \"has_uri_scheme\" implementation"); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + uri_scheme.to_glib_none().0, + ); + from_glib(ret) + } + } + + fn parent_uri_scheme(&self) -> Option { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .get_uri_scheme + .expect("no parent \"get_uri_scheme\" implementation"); + let ret = func(self.obj().unsafe_cast_ref::().to_glib_none().0); + from_glib_full(ret) + } + } + + fn parent_basename(&self) -> Option { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .get_basename + .expect("no parent \"get_basename\" implementation"); + let ret = func(self.obj().unsafe_cast_ref::().to_glib_none().0); + from_glib_full(ret) + } + } + + fn parent_path(&self) -> Option { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .get_path + .expect("no parent \"get_path\" implementation"); + let ret = func(self.obj().unsafe_cast_ref::().to_glib_none().0); + from_glib_full(ret) + } + } + + fn parent_uri(&self) -> String { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .get_uri + .expect("no parent \"get_uri\" implementation"); + let ret = func(self.obj().unsafe_cast_ref::().to_glib_none().0); + from_glib_full(ret) + } + } + + fn parent_parse_name(&self) -> String { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .get_parse_name + .expect("no parent \"get_parse_name\" implementation"); + let ret = func(self.obj().unsafe_cast_ref::().to_glib_none().0); + from_glib_full(ret) + } + } + + fn parent_parent(&self) -> Option { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .get_parent + .expect("no parent \"get_parent\" implementation"); + let ret = func(self.obj().unsafe_cast_ref::().to_glib_none().0); + from_glib_full(ret) + } + } + + fn parent_has_prefix(&self, prefix: &File) -> bool { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .prefix_matches + .expect("no parent \"prefix_matches\" implementation"); + let ret = func( + prefix.to_glib_none().0, + self.obj().unsafe_cast_ref::().to_glib_none().0, + ); + from_glib(ret) + } + } + + fn parent_relative_path(&self, descendant: &File) -> Option { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .get_relative_path + .expect("no parent \"get_relative_path\" implementation"); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + descendant.to_glib_none().0, + ); + from_glib_full(ret) + } + } + + fn parent_resolve_relative_path(&self, relative_path: impl AsRef) -> File { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .resolve_relative_path + .expect("no parent \"resolve_relative_path\" implementation"); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + relative_path.as_ref().to_glib_none().0, + ); + from_glib_full(ret) + } + } + + fn parent_child_for_display_name(&self, display_name: &str) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .get_child_for_display_name + .expect("no parent \"get_child_for_display_name\" implementation"); + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + display_name.to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } + } + + fn parent_enumerate_children( + &self, + attributes: &str, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).enumerate_children { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + attributes.to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_query_info( + &self, + attributes: &str, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).query_info { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + attributes.to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_query_filesystem_info( + &self, + attributes: &str, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).query_filesystem_info { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + attributes.to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_find_enclosing_mount( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).find_enclosing_mount { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotFound, + "Containing mount does not exist", + )) + } + } + } + + fn parent_set_display_name( + &self, + display_name: &str, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .set_display_name + .expect("no parent \"set_display_name\" implementation"); + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + display_name.to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } + } + + fn parent_query_settable_attributes( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).query_settable_attributes { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Ok(FileAttributeInfoList::new()) + } + } + } + + fn parent_query_writable_namespaces( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).query_writable_namespaces { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Ok(FileAttributeInfoList::new()) + } + } + } + + fn parent_set_attribute<'a>( + &self, + attribute: &str, + value: impl Into>, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).set_attribute { + let mut error = std::ptr::null_mut(); + let value: FileAttributeValue<'a> = value.into(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + attribute.to_glib_none().0, + value.type_().into_glib(), + value.as_ptr(), + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_set_attributes_from_info( + &self, + info: &FileInfo, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .set_attributes_from_info + .expect("no parent \"set_attributes_from_info\" implementation"); + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + info.to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + fn parent_read_fn( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).read_fn { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_append_to( + &self, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).append_to { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_create( + &self, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).create { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_replace( + &self, + etag: Option<&str>, + make_backup: bool, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).replace { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + etag.to_glib_none().0, + make_backup.into_glib(), + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_delete(&self, cancellable: Option<&impl IsA>) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).delete_file { + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_trash(&self, cancellable: Option<&impl IsA>) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).trash { + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_make_directory( + &self, + cancellable: Option<&impl IsA>, + ) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).make_directory { + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_make_symbolic_link( + &self, + symlink_value: impl AsRef, + cancellable: Option<&impl IsA>, + ) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).make_symbolic_link { + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + symlink_value.as_ref().to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_copy( + source: &File, + destination: &File, + flags: FileCopyFlags, + cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).copy { + let mut super_callback0 = progress_callback; + let (progress_callback, progress_callback_data) = super_callback0.as_mut().map_or( + (None, std::ptr::null_mut()), + |progress_callback| { + unsafe extern "C" fn progress_callback_trampoline( + current_num_bytes: i64, + total_num_bytes: i64, + user_data: glib::ffi::gpointer, + ) { + let progress_callback: &mut Box = + &mut *(user_data as *mut _); + progress_callback(current_num_bytes, total_num_bytes); + } + ( + Some(progress_callback_trampoline as _), + progress_callback as *mut Box as *mut _, + ) + }, + ); + + let mut error = std::ptr::null_mut(); + let is_ok = func( + source.to_glib_none().0, + destination.to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + progress_callback, + progress_callback_data, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else { + // give a chance to g_file_copy to call file_copy_fallback + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_move( + source: &File, + destination: &File, + flags: FileCopyFlags, + cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).move_ { + let mut super_callback0 = progress_callback; + let (progress_callback, progress_callback_data) = super_callback0.as_mut().map_or( + (None, std::ptr::null_mut()), + |progress_callback| { + unsafe extern "C" fn progress_callback_trampoline( + current_num_bytes: i64, + total_num_bytes: i64, + user_data: glib::ffi::gpointer, + ) { + let progress_callback: &mut Box = + &mut *(user_data as *mut _); + progress_callback(current_num_bytes, total_num_bytes); + } + ( + Some(progress_callback_trampoline as _), + progress_callback as *mut Box as *mut _, + ) + }, + ); + + let mut error = std::ptr::null_mut(); + let is_ok = func( + source.to_glib_none().0, + destination.to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + progress_callback, + progress_callback_data, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else { + // give a chance to g_file_move to call g_file_copy + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_mount_mountable( + &self, + flags: MountMountFlags, + mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + unsafe { + let (callback, user_data) = callback.map_or((None, std::ptr::null_mut()), |callback| { + let super_callback = Box::new(thread_guard::ThreadGuard::new(callback)); + + unsafe extern "C" fn callback_trampoline< + T: FileImpl, + P: FnOnce(&T::Type, &AsyncResult) + 'static, + >( + source_object: *mut glib::gobject_ffi::GObject, + res: *mut ffi::GAsyncResult, + data: glib::ffi::gpointer, + ) { + let source: &T::Type = &from_glib_borrow(source_object as *mut _); + let res: &AsyncResult = &from_glib_borrow(res); + let callback: Box> = Box::from_raw(data as *mut _); + let callback: P = callback.into_inner(); + callback(source, res); + } + let callback = callback_trampoline::; + + (Some(callback as _), Box::into_raw(super_callback) as *mut _) + }); + + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).mount_mountable { + func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + mount_operation.to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + callback, + user_data, + ); + } else { + ffi::g_task_report_new_error( + self.obj().unsafe_cast_ref::().to_glib_none().0, + callback, + user_data, + ffi::g_file_mount_mountable as *mut _, + IOErrorEnum::domain().into_glib(), + IOErrorEnum::NotSupported.into_glib(), + "Operation not supported".to_glib_full(), + ); + } + } + } + + fn parent_mount_mountable_finish(&self, res: &AsyncResult) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).mount_mountable_finish { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + res.to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else if let Some(task) = res.downcast_ref::>() { + // get the `Task` result as a `File` or as an error + task.to_owned().propagate() + } else { + // no parent implementation and don't know how to deal with the result so let's panic + panic!("no parent \"mount_mountable_finish\" implementation") + } + } + } + + fn parent_unmount_mountable( + &self, + flags: MountUnmountFlags, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + unsafe { + let (callback, user_data) = callback.map_or((None, std::ptr::null_mut()), |callback| { + let super_callback = Box::new(thread_guard::ThreadGuard::new(callback)); + + unsafe extern "C" fn callback_trampoline< + T: FileImpl, + P: FnOnce(&T::Type, &AsyncResult) + 'static, + >( + source_object: *mut glib::gobject_ffi::GObject, + res: *mut ffi::GAsyncResult, + data: glib::ffi::gpointer, + ) { + let source: &T::Type = &from_glib_borrow(source_object as *mut _); + let res: &AsyncResult = &from_glib_borrow(res); + let callback: Box> = Box::from_raw(data as *mut _); + let callback: P = callback.into_inner(); + callback(source, res); + } + let callback = callback_trampoline::; + + (Some(callback as _), Box::into_raw(super_callback) as *mut _) + }); + + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).unmount_mountable { + func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + callback, + user_data, + ); + } else { + ffi::g_task_report_new_error( + self.obj().unsafe_cast_ref::().to_glib_none().0, + callback, + user_data, + ffi::g_file_unmount_mountable_with_operation as *mut _, + IOErrorEnum::domain().into_glib(), + IOErrorEnum::NotSupported.into_glib(), + "Operation not supported".to_glib_full(), + ); + } + } + } + + fn parent_unmount_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).unmount_mountable_finish { + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + res.to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else if let Some(task) = res.downcast_ref::>() { + // get the `Task` result as a boolean or as an error + task.to_owned().propagate().map(|_| ()) + } else { + // no parent implementation and don't know how to deal with the result so let's panic + panic!("no parent \"unmount_mountable_finish\" implementation") + } + } + } + + fn parent_eject_mountable( + &self, + flags: MountUnmountFlags, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let (callback, user_data) = callback.map_or((None, std::ptr::null_mut()), |callback| { + let super_callback = Box::new(thread_guard::ThreadGuard::new(callback)); + + unsafe extern "C" fn callback_trampoline< + T: FileImpl, + P: FnOnce(&T::Type, &AsyncResult) + 'static, + >( + source_object: *mut glib::gobject_ffi::GObject, + res: *mut ffi::GAsyncResult, + data: glib::ffi::gpointer, + ) { + let source: &T::Type = &from_glib_borrow(source_object as *mut _); + let res: &AsyncResult = &from_glib_borrow(res); + let callback: Box> = Box::from_raw(data as *mut _); + let callback: P = callback.into_inner(); + callback(source, res); + } + let callback = callback_trampoline::; + + (Some(callback as _), Box::into_raw(super_callback) as *mut _) + }); + + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).eject_mountable { + func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + callback, + user_data, + ); + } else { + ffi::g_task_report_new_error( + self.obj().unsafe_cast_ref::().to_glib_none().0, + callback, + user_data, + ffi::g_file_eject_mountable_with_operation as *mut _, + IOErrorEnum::domain().into_glib(), + IOErrorEnum::NotSupported.into_glib(), + "Operation not supported".to_glib_full(), + ); + } + } + } + + fn parent_eject_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).eject_mountable_finish { + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + res.to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else if let Some(task) = res.downcast_ref::>() { + // get the `Task` result as a boolean or as an error + task.to_owned().propagate().map(|_| ()) + } else { + // no parent implementation and don't know how to deal with the result so let's panic + panic!("no parent \"eject_mountable_finish\" implementation") + } + } + } + + fn parent_mount_enclosing_volume( + &self, + flags: MountMountFlags, + mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let (callback, user_data) = callback.map_or((None, std::ptr::null_mut()), |callback| { + let super_callback = Box::new(thread_guard::ThreadGuard::new(callback)); + + unsafe extern "C" fn callback_trampoline< + T: FileImpl, + P: FnOnce(&T::Type, &AsyncResult) + 'static, + >( + source_object: *mut glib::gobject_ffi::GObject, + res: *mut ffi::GAsyncResult, + data: glib::ffi::gpointer, + ) { + let source: &T::Type = &from_glib_borrow(source_object as *mut _); + let res: &AsyncResult = &from_glib_borrow(res); + let callback: Box> = Box::from_raw(data as *mut _); + let callback: P = callback.into_inner(); + callback(source, res); + } + let callback = callback_trampoline::; + + (Some(callback as _), Box::into_raw(super_callback) as *mut _) + }); + + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).mount_enclosing_volume { + func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + mount_operation.to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + callback, + user_data, + ); + } else { + ffi::g_task_report_new_error( + self.obj().unsafe_cast_ref::().to_glib_none().0, + callback, + user_data, + ffi::g_file_mount_enclosing_volume as *mut _, + IOErrorEnum::domain().into_glib(), + IOErrorEnum::NotSupported.into_glib(), + "volume doesn’t implement mount enclosing volume".to_glib_full(), + ); + } + } + } + + fn parent_mount_enclosing_volume_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).mount_enclosing_volume_finish { + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + res.to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else if let Some(task) = res.downcast_ref::>() { + // get the `Task` result as a boolean or as an error + task.to_owned().propagate().map(|_| ()) + } else { + // no parent implementation and don't know how to deal with the result so let's panic + panic!("no parent \"mount_enclosing_volume_finish\" implementation") + } + } + } + + fn parent_monitor_dir( + &self, + flags: FileMonitorFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).monitor_dir { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_monitor_file( + &self, + flags: FileMonitorFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).monitor_file { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + // cannot call private _g_poll_file_monitor_new + panic!("no parent \"monitor_file\" implementation") + } + } + } + + fn parent_open_readwrite( + &self, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).open_readwrite { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_create_readwrite( + &self, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).create_readwrite { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_replace_readwrite( + &self, + etag: Option<&str>, + make_backup: bool, + flags: FileCreateFlags, + cancellable: Option<&impl IsA>, + ) -> Result { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).replace_readwrite { + let mut error = std::ptr::null_mut(); + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + etag.to_glib_none().0, + make_backup.into_glib(), + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } else { + Err(Error::new::( + IOErrorEnum::NotSupported, + "Operation not supported", + )) + } + } + } + + fn parent_start_mountable( + &self, + flags: DriveStartFlags, + mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let (callback, user_data) = callback.map_or((None, std::ptr::null_mut()), |callback| { + let super_callback = Box::new(thread_guard::ThreadGuard::new(callback)); + + unsafe extern "C" fn callback_trampoline< + T: FileImpl, + P: FnOnce(&T::Type, &AsyncResult) + 'static, + >( + source_object: *mut glib::gobject_ffi::GObject, + res: *mut ffi::GAsyncResult, + data: glib::ffi::gpointer, + ) { + let source: &T::Type = &from_glib_borrow(source_object as *mut _); + let res: &AsyncResult = &from_glib_borrow(res); + let callback: Box> = Box::from_raw(data as *mut _); + let callback: P = callback.into_inner(); + callback(source, res); + } + let callback = callback_trampoline::; + + (Some(callback as _), Box::into_raw(super_callback) as *mut _) + }); + + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).start_mountable { + func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + mount_operation.to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + callback, + user_data, + ); + } else { + ffi::g_task_report_new_error( + self.obj().unsafe_cast_ref::().to_glib_none().0, + callback, + user_data, + ffi::g_file_start_mountable as *mut _, + IOErrorEnum::domain().into_glib(), + IOErrorEnum::NotSupported.into_glib(), + "Operation not supported".to_glib_full(), + ); + } + } + } + + fn parent_start_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).start_mountable_finish { + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + res.to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else if let Some(task) = res.downcast_ref::>() { + // get the `Task` result as a boolean or as an error + task.to_owned().propagate().map(|_| ()) + } else { + // no parent implementation and don't know how to deal with the result so let's panic + panic!("no parent \"start_mountable_finish\" implementation") + } + } + } + + fn parent_stop_mountable( + &self, + flags: MountUnmountFlags, + mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let (callback, user_data) = callback.map_or((None, std::ptr::null_mut()), |callback| { + let super_callback = Box::new(thread_guard::ThreadGuard::new(callback)); + + unsafe extern "C" fn callback_trampoline< + T: FileImpl, + P: FnOnce(&T::Type, &AsyncResult) + 'static, + >( + source_object: *mut glib::gobject_ffi::GObject, + res: *mut ffi::GAsyncResult, + data: glib::ffi::gpointer, + ) { + let source: &T::Type = &from_glib_borrow(source_object as *mut _); + let res: &AsyncResult = &from_glib_borrow(res); + let callback: Box> = Box::from_raw(data as *mut _); + let callback: P = callback.into_inner(); + callback(source, res); + } + let callback = callback_trampoline::; + + (Some(callback as _), Box::into_raw(super_callback) as *mut _) + }); + + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).stop_mountable { + func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + mount_operation.to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + callback, + user_data, + ); + } else { + ffi::g_task_report_new_error( + self.obj().unsafe_cast_ref::().to_glib_none().0, + callback, + user_data, + ffi::g_file_stop_mountable as *mut _, + IOErrorEnum::domain().into_glib(), + IOErrorEnum::NotSupported.into_glib(), + "Operation not supported".to_glib_full(), + ); + } + } + } + + fn parent_stop_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).stop_mountable_finish { + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + res.to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else if let Some(task) = res.downcast_ref::>() { + // get the `Task` result as a boolean or as an error + task.to_owned().propagate().map(|_| ()) + } else { + // no parent implementation and don't know how to deal with the result so let's panic + panic!("no parent \"stop_mountable_finish\" implementation") + } + } + } + + fn parent_unmount_mountable_with_operation( + &self, + flags: MountUnmountFlags, + mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).unmount_mountable_with_operation { + let (callback, user_data) = + callback.map_or((None, std::ptr::null_mut()), |callback| { + let super_callback = Box::new(thread_guard::ThreadGuard::new(callback)); + + unsafe extern "C" fn callback_trampoline< + T: FileImpl, + P: FnOnce(&T::Type, &AsyncResult) + 'static, + >( + source_object: *mut glib::gobject_ffi::GObject, + res: *mut ffi::GAsyncResult, + data: glib::ffi::gpointer, + ) { + let source_object: &T::Type = + &from_glib_borrow(source_object as *mut _); + let res: &AsyncResult = &from_glib_borrow(res); + let callback: Box> = + Box::from_raw(data as *mut _); + let callback: P = callback.into_inner(); + callback(source_object, res); + } + let callback = callback_trampoline::; + + (Some(callback as _), Box::into_raw(super_callback) as *mut _) + }); + + func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + mount_operation.to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + callback, + user_data, + ) + } else { + self.unmount_mountable(flags, cancellable, callback); + } + } + } + + fn parent_unmount_mountable_with_operation_finish( + &self, + res: &AsyncResult, + ) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).unmount_mountable_with_operation_finish { + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + res.to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else { + self.unmount_mountable_finish(res) + } + } + } + + fn parent_eject_mountable_with_operation( + &self, + flags: MountUnmountFlags, + mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).eject_mountable_with_operation { + let (callback, user_data) = + callback.map_or((None, std::ptr::null_mut()), |callback| { + let super_callback = Box::new(thread_guard::ThreadGuard::new(callback)); + + unsafe extern "C" fn callback_trampoline< + T: FileImpl, + P: FnOnce(&T::Type, &AsyncResult) + 'static, + >( + source_object: *mut glib::gobject_ffi::GObject, + res: *mut ffi::GAsyncResult, + data: glib::ffi::gpointer, + ) { + let source: &T::Type = &from_glib_borrow(source_object as *mut _); + let res: &AsyncResult = &from_glib_borrow(res); + let callback: Box> = + Box::from_raw(data as *mut _); + let callback: P = callback.into_inner(); + callback(source, res); + } + let callback = callback_trampoline::; + + (Some(callback as _), Box::into_raw(super_callback) as *mut _) + }); + + func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + mount_operation.to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + callback, + user_data, + ); + } else { + self.eject_mountable(flags, cancellable, callback); + } + } + } + + fn parent_eject_mountable_with_operation_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).eject_mountable_with_operation_finish { + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + res.to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else { + self.eject_mountable_finish(res) + } + } + } + + fn parent_poll_mountable( + &self, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let (callback, user_data) = callback.map_or((None, std::ptr::null_mut()), |callback| { + let super_callback = Box::new(thread_guard::ThreadGuard::new(callback)); + + unsafe extern "C" fn callback_trampoline< + T: FileImpl, + P: FnOnce(&T::Type, &AsyncResult) + 'static, + >( + source_object: *mut glib::gobject_ffi::GObject, + res: *mut ffi::GAsyncResult, + data: glib::ffi::gpointer, + ) { + let source: &T::Type = &from_glib_borrow(source_object as *mut _); + let res: &AsyncResult = &from_glib_borrow(res); + let callback: Box> = Box::from_raw(data as *mut _); + let callback: P = callback.into_inner(); + callback(source, res); + } + let callback = callback_trampoline::; + + (Some(callback as _), Box::into_raw(super_callback) as *mut _) + }); + + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).poll_mountable { + func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + callback, + user_data, + ); + } else { + ffi::g_task_report_new_error( + self.obj().unsafe_cast_ref::().to_glib_none().0, + callback, + user_data, + ffi::g_file_poll_mountable as *mut _, + IOErrorEnum::domain().into_glib(), + IOErrorEnum::NotSupported.into_glib(), + "Operation not supported".to_glib_full(), + ); + } + } + } + + fn parent_poll_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).poll_mountable_finish { + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + res.to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } else if let Some(task) = res.downcast_ref::>() { + // get the `Task` result as a boolean or as an error + task.to_owned().propagate().map(|_| ()) + } else { + // no parent implementation and don't know how to deal with the result so let's panic + panic!("no parent \"poll_mountable_finish\" implementation") + } + } + } + + fn parent_measure_disk_usage( + &self, + flags: FileMeasureFlags, + cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(u64, u64, u64), Error> { + let mut super_callback0 = progress_callback; + let (progress_callback, progress_callback_data) = + super_callback0 + .as_mut() + .map_or((None, std::ptr::null_mut()), |progress_callback| { + unsafe extern "C" fn progress_callback_trampoline( + reporting: glib::ffi::gboolean, + current_size: u64, + num_dirs: u64, + num_files: u64, + user_data: glib::ffi::gpointer, + ) { + let progress_callback: &mut Box = + &mut *(user_data as *mut _); + progress_callback(from_glib(reporting), current_size, num_dirs, num_files) + } + ( + Some(progress_callback_trampoline as _), + progress_callback as *mut Box + as *mut _, + ) + }); + + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + let func = (*parent_iface) + .measure_disk_usage + .expect("no parent \"measure_disk_usage\" implementation"); + let mut disk_usage = 0u64; + let mut num_dirs = 0u64; + let mut num_files = 0u64; + let mut error = std::ptr::null_mut(); + let is_ok = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + progress_callback, + progress_callback_data, + &mut disk_usage, + &mut num_dirs, + &mut num_files, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok((disk_usage, num_dirs, num_files)) + } else { + Err(from_glib_full(error)) + } + } + } + + fn parent_query_exists(&self, cancellable: Option<&impl IsA>) -> bool { + unsafe { + let type_data = Self::type_data(); + let parent_iface = + type_data.as_ref().parent_interface::() as *const ffi::GFileIface; + + if let Some(func) = (*parent_iface).query_exists { + let ret = func( + self.obj().unsafe_cast_ref::().to_glib_none().0, + cancellable.map(|p| p.as_ref()).to_glib_none().0, + ); + from_glib(ret) + } else { + let file_info = + self.query_info("standard::type", FileQueryInfoFlags::NONE, cancellable); + file_info.is_ok() + } + } + } +} + +impl FileImplExt for T {} + +// Implement virtual functions defined in `gio::ffi::GFileIface` except pairs `xxx_async/xxx_finish` for which GIO provides a default implementation. +unsafe impl IsImplementable for File { + fn interface_init(iface: &mut Interface) { + let iface = iface.as_mut(); + + iface.dup = Some(file_dup::); + iface.hash = Some(file_hash::); + iface.equal = Some(file_equal::); + iface.is_native = Some(file_is_native::); + iface.has_uri_scheme = Some(file_has_uri_scheme::); + iface.get_uri_scheme = Some(file_get_uri_scheme::); + iface.get_basename = Some(file_get_basename::); + iface.get_path = Some(file_get_path::); + iface.get_uri = Some(file_get_uri::); + iface.get_parse_name = Some(file_get_parse_name::); + iface.get_parent = Some(file_get_parent::); + iface.prefix_matches = Some(file_prefix_matches::); + iface.get_relative_path = Some(file_get_relative_path::); + iface.resolve_relative_path = Some(file_resolve_relative_path::); + iface.get_child_for_display_name = Some(file_get_child_for_display_name::); + iface.enumerate_children = Some(file_enumerate_children::); + iface.query_info = Some(file_query_info::); + iface.query_filesystem_info = Some(file_query_filesystem_info::); + iface.find_enclosing_mount = Some(file_find_enclosing_mount::); + iface.set_display_name = Some(file_set_display_name::); + iface.query_settable_attributes = Some(file_query_settable_attributes::); + iface.query_writable_namespaces = Some(file_query_writable_namespaces::); + iface.set_attribute = Some(file_set_attribute::); + iface.set_attributes_from_info = Some(file_set_attributes_from_info::); + iface.read_fn = Some(file_read_fn::); + iface.append_to = Some(file_append_to::); + iface.create = Some(file_create::); + iface.replace = Some(file_replace::); + iface.delete_file = Some(file_delete_file::); + iface.trash = Some(file_trash::); + iface.make_directory = Some(file_make_directory::); + iface.make_symbolic_link = Some(file_make_symbolic_link::); + iface.copy = Some(file_copy::); + iface.move_ = Some(file_move::); + iface.mount_mountable = Some(file_mount_mountable::); + iface.mount_mountable_finish = Some(file_mount_mountable_finish::); + iface.unmount_mountable = Some(file_unmount_mountable::); + iface.unmount_mountable_finish = Some(file_unmount_mountable_finish::); + iface.eject_mountable = Some(file_eject_mountable::); + iface.eject_mountable_finish = Some(file_eject_mountable_finish::); + iface.mount_enclosing_volume = Some(file_mount_enclosing_volume::); + iface.mount_enclosing_volume_finish = Some(file_mount_enclosing_volume_finish::); + iface.monitor_dir = Some(file_monitor_dir::); + iface.monitor_file = Some(file_monitor_file::); + iface.open_readwrite = Some(file_open_readwrite::); + iface.create_readwrite = Some(file_create_readwrite::); + iface.replace_readwrite = Some(file_replace_readwrite::); + iface.start_mountable = Some(file_start_mountable::); + iface.start_mountable_finish = Some(file_start_mountable_finish::); + iface.stop_mountable = Some(file_stop_mountable::); + iface.stop_mountable_finish = Some(file_stop_mountable_finish::); + iface.supports_thread_contexts = T::SUPPORT_THREAD_CONTEXT.into_glib(); + iface.unmount_mountable_with_operation = Some(file_unmount_mountable_with_operation::); + iface.unmount_mountable_with_operation_finish = + Some(file_unmount_mountable_with_operation_finish::); + iface.eject_mountable_with_operation = Some(file_eject_mountable_with_operation::); + iface.eject_mountable_with_operation_finish = + Some(file_eject_mountable_with_operation_finish::); + iface.poll_mountable = Some(file_poll_mountable::); + iface.poll_mountable_finish = Some(file_poll_mountable_finish::); + iface.measure_disk_usage = Some(file_measure_disk_usage::); + #[cfg(feature = "v2_84")] + { + iface.query_exists = Some(file_query_exists::); + } + // `GFile` already implements `xxx_async/xxx_finish` vfuncs and this should be ok. + // TODO: when needed, override the `GFile` implementation of the following vfuncs: + // iface.enumerate_children_async = Some(file_enumerate_children_async::); + // iface.enumerate_children_finish = Some(file_enumerate_children_finish::); + // iface.query_info_async = Some(file_query_info_async::); + // iface.query_info_finish = Some(file_query_info_finish::); + // iface.query_filesystem_info_async = Some(file_query_filesystem_info_async::); + // iface.query_filesystem_info_finish = Some(file_query_filesystem_info_finish::); + // iface.find_enclosing_mount_async = Some(file_find_enclosing_mount_asyncv); + // iface.find_enclosing_mount_finish = Some(file_find_enclosing_mount_finish::); + // iface.set_display_name_async = Some(file_set_display_name_async::); + // iface.set_display_name_finish = Some(file_set_display_name_finish::); + // iface._query_settable_attributes_async = Some(_file_query_settable_attributes_async::); + // iface._query_settable_attributes_finish = Some(_file_query_settable_attributes_finish::); + // iface._query_writable_namespaces_async = Some(_file_query_writable_namespaces_async::); + // iface._query_writable_namespaces_finish = Some(_file_query_writable_namespaces_finish::); + // iface.set_attributes_async = Some(file_set_attributes_async::); + // iface.set_attributes_finish = Some(file_set_attributes_finishv); + // iface.read_async = Some(file_read_async::); + // iface.read_finish = Some(file_read_finish::); + // iface.append_to_async = Some(file_append_to_async::); + // iface.append_to_finish = Some(file_append_to_finish::); + // iface.create_async = Some(file_create_async::); + // iface.create_finish = Some(file_create_finish::); + // iface.replace_async = Some(file_replace_async::); + // iface.replace_finish = Some(file_replace_finish::); + // iface.delete_file_async = Some(file_delete_file_async::); + // iface.delete_file_finish = Some(file_delete_file_finish::); + // iface.trash_async = Some(file_trash_async::); + // iface.trash_finish = Some(file_trash_finish::); + // iface.make_directory_async = Some(file_make_directory_async::); + // iface.make_directory_finish = Some(file_make_directory_finish::); + // iface.make_symbolic_link_async = Some(file_make_symbolic_link_async::); + // iface.make_symbolic_link_finish = Some(file_make_symbolic_link_finish::); + // iface.copy_async = Some(file_copy_async::); + // iface.copy_finish = Some(file_copy_finish::); + // iface.move_async = Some(file_move_async::); + // iface.move_finish = Some(file_move_finish::); + // iface.open_readwrite_async = Some(file_open_readwrite_async::); + // iface.open_readwrite_finish = Some(file_open_readwrite_finish::); + // iface.create_readwrite_async = Some(file_create_readwrite_async::); + // iface.create_readwrite_finish = Some(file_create_readwrite_finish::); + // iface.replace_readwrite_async = Some(file_replace_readwrite_async::); + // iface.replace_readwrite_finish = Some(file_replace_readwrite_finish::); + // iface.measure_disk_usage_async = Some(file_measure_disk_usage_async::); + // iface.measure_disk_usage_finish = Some(file_measure_disk_usage_finish::); + } +} + +unsafe extern "C" fn file_dup(file: *mut ffi::GFile) -> *mut ffi::GFile { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + imp.dup().to_glib_full() +} + +unsafe extern "C" fn file_hash(file: *mut ffi::GFile) -> c_uint { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + imp.hash() +} + +unsafe extern "C" fn file_equal( + file1: *mut ffi::GFile, + file2: *mut ffi::GFile, +) -> glib::ffi::gboolean { + let instance = &*(file1 as *mut T::Instance); + let imp = instance.imp(); + + imp.equal(&from_glib_borrow(file2)).into_glib() +} + +unsafe extern "C" fn file_is_native(file: *mut ffi::GFile) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + imp.is_native().into_glib() +} + +unsafe extern "C" fn file_has_uri_scheme( + file: *mut ffi::GFile, + uri_scheme: *const c_char, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + imp.has_uri_scheme(&GString::from_glib_borrow(uri_scheme)) + .into_glib() +} + +unsafe extern "C" fn file_get_uri_scheme(file: *mut ffi::GFile) -> *mut c_char { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + let res = imp.uri_scheme(); + if let Some(uri_scheme) = res { + uri_scheme.to_glib_full() + } else { + std::ptr::null_mut() + } +} + +unsafe extern "C" fn file_get_basename(file: *mut ffi::GFile) -> *mut c_char { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + let res = imp.basename(); + if let Some(basename) = res { + basename.to_glib_full() + } else { + std::ptr::null_mut() + } +} + +unsafe extern "C" fn file_get_path(file: *mut ffi::GFile) -> *mut c_char { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + let res = imp.path(); + if let Some(path) = res { + path.to_glib_full() + } else { + std::ptr::null_mut() + } +} + +unsafe extern "C" fn file_get_uri(file: *mut ffi::GFile) -> *mut c_char { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + let uri = imp.uri(); + uri.to_glib_full() +} + +unsafe extern "C" fn file_get_parse_name(file: *mut ffi::GFile) -> *mut c_char { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + let parse_name = imp.parse_name(); + parse_name.to_glib_full() +} + +unsafe extern "C" fn file_get_parent(file: *mut ffi::GFile) -> *mut ffi::GFile { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + let res = imp.parent(); + if let Some(parent) = res { + parent.to_glib_full() + } else { + std::ptr::null_mut() + } +} + +unsafe extern "C" fn file_prefix_matches( + prefix: *mut ffi::GFile, + file: *mut ffi::GFile, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + imp.has_prefix(&from_glib_borrow(prefix)).into_glib() +} + +unsafe extern "C" fn file_get_relative_path( + parent: *mut ffi::GFile, + descendant: *mut ffi::GFile, +) -> *mut c_char { + let instance = &*(parent as *mut T::Instance); + let imp = instance.imp(); + + let res = imp.relative_path(&from_glib_borrow(descendant)); + if let Some(relative_path) = res { + relative_path.to_glib_full() + } else { + std::ptr::null_mut() + } +} + +unsafe extern "C" fn file_resolve_relative_path( + file: *mut ffi::GFile, + relative_path: *const c_char, +) -> *mut ffi::GFile { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + let resolved_path = + imp.resolve_relative_path(GString::from_glib_borrow(relative_path).as_ref()); + resolved_path.to_glib_full() +} + +unsafe extern "C" fn file_get_child_for_display_name( + file: *mut ffi::GFile, + display_name: *const c_char, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFile { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + + // check display name is a valid ut8 and handle error to avoid rust panicking if it is not + let basename = glib::ffi::g_filename_from_utf8( + display_name, + -1, + std::ptr::null_mut(), + std::ptr::null_mut(), + std::ptr::null_mut(), + ); + if basename.is_null() { + if !error.is_null() { + *error = Error::new::(IOErrorEnum::InvalidFilename, "Invalid filename") + .to_glib_full(); + } + return std::ptr::null_mut(); + } + + let res = imp.child_for_display_name(&GString::from_glib_borrow(display_name)); + match res { + Ok(child) => child.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_enumerate_children( + file: *mut ffi::GFile, + attributes: *const c_char, + flags: ffi::GFileQueryInfoFlags, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileEnumerator { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.enumerate_children( + &GString::from_glib_borrow(attributes), + from_glib(flags), + cancellable.as_ref(), + ); + match res { + Ok(enumerator) => enumerator.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_query_info( + file: *mut ffi::GFile, + attributes: *const c_char, + flags: ffi::GFileQueryInfoFlags, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileInfo { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.query_info( + &GString::from_glib_borrow(attributes), + from_glib(flags), + cancellable.as_ref(), + ); + match res { + Ok(file_info) => file_info.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_query_filesystem_info( + file: *mut ffi::GFile, + attributes: *const c_char, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileInfo { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = + imp.query_filesystem_info(&GString::from_glib_borrow(attributes), cancellable.as_ref()); + match res { + Ok(file_info) => file_info.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_find_enclosing_mount( + file: *mut ffi::GFile, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GMount { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.find_enclosing_mount(cancellable.as_ref()); + match res { + Ok(mount) => mount.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_set_display_name( + file: *mut ffi::GFile, + display_name: *const c_char, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFile { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.set_display_name( + &GString::from_glib_borrow(display_name), + cancellable.as_ref(), + ); + match res { + Ok(renamed_file) => renamed_file.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_query_settable_attributes( + file: *mut ffi::GFile, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileAttributeInfoList { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.query_settable_attributes(cancellable.as_ref()); + match res { + Ok(settable_attributes) => settable_attributes.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_query_writable_namespaces( + file: *mut ffi::GFile, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileAttributeInfoList { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.query_writable_namespaces(cancellable.as_ref()); + match res { + Ok(writable_namespaces) => writable_namespaces.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_set_attribute( + file: *mut ffi::GFile, + attribute: *const c_char, + type_: ffi::GFileAttributeType, + value_p: glib::ffi::gpointer, + flags: ffi::GFileQueryInfoFlags, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res_ = imp.set_attribute( + &GString::from_glib_borrow(attribute), + FileAttributeValue::for_pointer(from_glib(type_), value_p), + from_glib(flags), + cancellable.as_ref(), + ); + + match res_ { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_set_attributes_from_info( + file: *mut ffi::GFile, + info: *mut ffi::GFileInfo, + flags: ffi::GFileQueryInfoFlags, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res_ = imp.set_attributes_from_info( + &from_glib_borrow(info), + from_glib(flags), + cancellable.as_ref(), + ); + match res_ { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_read_fn( + file: *mut ffi::GFile, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileInputStream { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res_ = imp.read_fn(cancellable.as_ref()); + match res_ { + Ok(input_stream) => input_stream.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_append_to( + file: *mut ffi::GFile, + flags: ffi::GFileCreateFlags, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileOutputStream { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res_ = imp.append_to(from_glib(flags), cancellable.as_ref()); + match res_ { + Ok(output_stream) => output_stream.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_create( + file: *mut ffi::GFile, + flags: ffi::GFileCreateFlags, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileOutputStream { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res_ = imp.create(from_glib(flags), cancellable.as_ref()); + match res_ { + Ok(output_stream) => output_stream.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_replace( + file: *mut ffi::GFile, + etag: *const c_char, + make_backup: glib::ffi::gboolean, + flags: ffi::GFileCreateFlags, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileOutputStream { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let etag = Option::::from_glib_none(etag); + let cancellable = Option::::from_glib_none(cancellable); + + let res_ = imp.replace( + etag.as_ref().map(|etag| etag.as_str()), + from_glib(make_backup), + from_glib(flags), + cancellable.as_ref(), + ); + match res_ { + Ok(output_stream) => output_stream.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_delete_file( + file: *mut ffi::GFile, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.delete(cancellable.as_ref()); + match res { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_trash( + file: *mut ffi::GFile, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.trash(cancellable.as_ref()); + match res { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_make_directory( + file: *mut ffi::GFile, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.make_directory(cancellable.as_ref()); + match res { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_make_symbolic_link( + file: *mut ffi::GFile, + symlink_value: *const c_char, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.make_symbolic_link( + GString::from_glib_borrow(symlink_value).as_ref(), + cancellable.as_ref(), + ); + match res { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_copy( + source: *mut ffi::GFile, + destination: *mut ffi::GFile, + flags: ffi::GFileCopyFlags, + cancellable: *mut ffi::GCancellable, + progress_callback: ffi::GFileProgressCallback, + progress_callback_data: glib::ffi::gpointer, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let cancellable = Option::::from_glib_none(cancellable); + let progress_callback = progress_callback.map(|callback| { + let boxed: Box = + Box::new(move |current_num_bytes, total_num_bytes| unsafe { + callback(current_num_bytes, total_num_bytes, progress_callback_data) + }); + boxed + }); + + let res = T::copy( + &from_glib_borrow(source), + &from_glib_borrow(destination), + from_glib(flags), + cancellable.as_ref(), + progress_callback, + ); + match res { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_move( + source: *mut ffi::GFile, + destination: *mut ffi::GFile, + flags: ffi::GFileCopyFlags, + cancellable: *mut ffi::GCancellable, + progress_callback: ffi::GFileProgressCallback, + progress_callback_data: glib::ffi::gpointer, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let cancellable = Option::::from_glib_none(cancellable); + let progress_callback = progress_callback.map(|callback| { + let boxed: Box = + Box::new(move |current_num_bytes, total_num_bytes| unsafe { + callback(current_num_bytes, total_num_bytes, progress_callback_data) + }); + boxed + }); + + let res = T::move_( + &from_glib_borrow(source), + &from_glib_borrow(destination), + from_glib(flags), + cancellable.as_ref(), + progress_callback, + ); + match res { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_mount_mountable( + file: *mut ffi::GFile, + flags: ffi::GMountMountFlags, + mount_operation: *mut ffi::GMountOperation, + cancellable: *mut ffi::GCancellable, + callback: ffi::GAsyncReadyCallback, + user_data: glib::ffi::gpointer, +) { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let mount_operation = Option::::from_glib_none(mount_operation); + let cancellable = Option::::from_glib_none(cancellable); + let callback = callback.map(|callback| { + move |source: &T::Type, res: &AsyncResult| unsafe { + callback( + source.upcast_ref::().to_glib_none().0, + res.to_glib_none().0, + user_data, + ) + } + }); + + imp.mount_mountable( + from_glib(flags), + mount_operation.as_ref(), + cancellable.as_ref(), + callback, + ); +} + +unsafe extern "C" fn file_mount_mountable_finish( + file: *mut ffi::GFile, + res: *mut ffi::GAsyncResult, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFile { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let result: &AsyncResult = &from_glib_borrow(res); + + let res_ = imp.mount_mountable_finish(result); + match res_ { + Ok(mounted) => mounted.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_unmount_mountable( + file: *mut ffi::GFile, + flags: ffi::GMountUnmountFlags, + cancellable: *mut ffi::GCancellable, + callback: ffi::GAsyncReadyCallback, + user_data: glib::ffi::gpointer, +) { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + let callback = callback.map(|callback| { + move |source: &T::Type, res: &AsyncResult| unsafe { + callback( + source.upcast_ref::().to_glib_none().0, + res.to_glib_none().0, + user_data, + ) + } + }); + + imp.unmount_mountable(from_glib(flags), cancellable.as_ref(), callback); +} + +unsafe extern "C" fn file_unmount_mountable_finish( + file: *mut ffi::GFile, + res: *mut ffi::GAsyncResult, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let result: &AsyncResult = &from_glib_borrow(res); + + let res_ = imp.unmount_mountable_finish(result); + match res_ { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_eject_mountable( + file: *mut ffi::GFile, + flags: ffi::GMountUnmountFlags, + cancellable: *mut ffi::GCancellable, + callback: ffi::GAsyncReadyCallback, + user_data: glib::ffi::gpointer, +) { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + let callback = callback.map(|callback| { + move |source: &T::Type, res: &AsyncResult| unsafe { + callback( + source.upcast_ref::().to_glib_none().0, + res.to_glib_none().0, + user_data, + ) + } + }); + + imp.eject_mountable(from_glib(flags), cancellable.as_ref(), callback); +} + +unsafe extern "C" fn file_eject_mountable_finish( + file: *mut ffi::GFile, + res: *mut ffi::GAsyncResult, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let result: &AsyncResult = &from_glib_borrow(res); + + let res_ = imp.eject_mountable_finish(result); + match res_ { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_mount_enclosing_volume( + file: *mut ffi::GFile, + flags: ffi::GMountMountFlags, + mount_operation: *mut ffi::GMountOperation, + cancellable: *mut ffi::GCancellable, + callback: ffi::GAsyncReadyCallback, + user_data: glib::ffi::gpointer, +) { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let mount_operation = Option::::from_glib_none(mount_operation); + let cancellable = Option::::from_glib_none(cancellable); + let callback = callback.map(|callback| { + move |source: &T::Type, res: &AsyncResult| unsafe { + callback( + source.upcast_ref::().to_glib_none().0, + res.to_glib_none().0, + user_data, + ) + } + }); + + imp.mount_enclosing_volume( + from_glib(flags), + mount_operation.as_ref(), + cancellable.as_ref(), + callback, + ); +} + +unsafe extern "C" fn file_mount_enclosing_volume_finish( + file: *mut ffi::GFile, + res: *mut ffi::GAsyncResult, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let result: &AsyncResult = &from_glib_borrow(res); + + let res_ = imp.mount_enclosing_volume_finish(result); + match res_ { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_monitor_dir( + file: *mut ffi::GFile, + flags: ffi::GFileMonitorFlags, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileMonitor { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.monitor_dir(from_glib(flags), cancellable.as_ref()); + match res { + Ok(monitor) => monitor.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_monitor_file( + file: *mut ffi::GFile, + flags: ffi::GFileMonitorFlags, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileMonitor { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.monitor_file(from_glib(flags), cancellable.as_ref()); + match res { + Ok(monitor) => monitor.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_open_readwrite( + file: *mut ffi::GFile, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileIOStream { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.open_readwrite(cancellable.as_ref()); + match res { + Ok(io_stream) => io_stream.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_create_readwrite( + file: *mut ffi::GFile, + flags: ffi::GFileCreateFlags, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileIOStream { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.create_readwrite(from_glib(flags), cancellable.as_ref()); + match res { + Ok(io_stream) => io_stream.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_replace_readwrite( + file: *mut ffi::GFile, + etag: *const c_char, + make_backup: glib::ffi::gboolean, + flags: ffi::GFileCreateFlags, + cancellable: *mut ffi::GCancellable, + error: *mut *mut glib::ffi::GError, +) -> *mut ffi::GFileIOStream { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let etag = Option::::from_glib_none(etag); + let cancellable = Option::::from_glib_none(cancellable); + + let res_ = imp.replace_readwrite( + etag.as_ref().map(|etag| etag.as_str()), + from_glib(make_backup), + from_glib(flags), + cancellable.as_ref(), + ); + match res_ { + Ok(io_stream) => io_stream.to_glib_full(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + std::ptr::null_mut() + } + } +} + +unsafe extern "C" fn file_start_mountable( + file: *mut ffi::GFile, + flags: ffi::GDriveStartFlags, + mount_operation: *mut ffi::GMountOperation, + cancellable: *mut ffi::GCancellable, + callback: ffi::GAsyncReadyCallback, + user_data: glib::ffi::gpointer, +) { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let mount_operation = Option::::from_glib_none(mount_operation); + let cancellable = Option::::from_glib_none(cancellable); + let callback = callback.map(|callback| { + move |source: &T::Type, res: &AsyncResult| unsafe { + callback( + source.upcast_ref::().to_glib_none().0, + res.to_glib_none().0, + user_data, + ) + } + }); + + imp.start_mountable( + from_glib(flags), + mount_operation.as_ref(), + cancellable.as_ref(), + callback, + ); +} + +unsafe extern "C" fn file_start_mountable_finish( + file: *mut ffi::GFile, + res: *mut ffi::GAsyncResult, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let result: &AsyncResult = &from_glib_borrow(res); + + let res_ = imp.start_mountable_finish(result); + match res_ { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_stop_mountable( + file: *mut ffi::GFile, + flags: ffi::GMountUnmountFlags, + mount_operation: *mut ffi::GMountOperation, + cancellable: *mut ffi::GCancellable, + callback: ffi::GAsyncReadyCallback, + user_data: glib::ffi::gpointer, +) { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let mount_operation = Option::::from_glib_none(mount_operation); + let cancellable = Option::::from_glib_none(cancellable); + let callback = callback.map(|callback| { + move |source: &T::Type, res: &AsyncResult| unsafe { + callback( + source.upcast_ref::().to_glib_none().0, + res.to_glib_none().0, + user_data, + ) + } + }); + + imp.stop_mountable( + from_glib(flags), + mount_operation.as_ref(), + cancellable.as_ref(), + callback, + ); +} + +unsafe extern "C" fn file_stop_mountable_finish( + file: *mut ffi::GFile, + res: *mut ffi::GAsyncResult, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let result: &AsyncResult = &from_glib_borrow(res); + + let res_ = imp.stop_mountable_finish(result); + match res_ { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_unmount_mountable_with_operation( + file: *mut ffi::GFile, + flags: ffi::GMountUnmountFlags, + mount_operation: *mut ffi::GMountOperation, + cancellable: *mut ffi::GCancellable, + callback: ffi::GAsyncReadyCallback, + user_data: glib::ffi::gpointer, +) { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let mount_operation = Option::::from_glib_none(mount_operation); + let cancellable = Option::::from_glib_none(cancellable); + let callback = callback.map(|callback| { + move |source: &T::Type, res: &AsyncResult| unsafe { + callback( + source.upcast_ref::().to_glib_none().0, + res.to_glib_none().0, + user_data, + ) + } + }); + + imp.unmount_mountable_with_operation( + from_glib(flags), + mount_operation.as_ref(), + cancellable.as_ref(), + callback, + ); +} + +unsafe extern "C" fn file_unmount_mountable_with_operation_finish( + file: *mut ffi::GFile, + res: *mut ffi::GAsyncResult, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let result: &AsyncResult = &from_glib_borrow(res); + + let res_ = imp.unmount_mountable_with_operation_finish(result); + match res_ { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_eject_mountable_with_operation( + file: *mut ffi::GFile, + flags: ffi::GMountUnmountFlags, + mount_operation: *mut ffi::GMountOperation, + cancellable: *mut ffi::GCancellable, + callback: ffi::GAsyncReadyCallback, + user_data: glib::ffi::gpointer, +) { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let mount_operation = Option::::from_glib_none(mount_operation); + let cancellable = Option::::from_glib_none(cancellable); + let callback = callback.map(|callback| { + move |source: &T::Type, res: &AsyncResult| unsafe { + callback( + source.upcast_ref::().to_glib_none().0, + res.to_glib_none().0, + user_data, + ) + } + }); + + imp.eject_mountable_with_operation( + from_glib(flags), + mount_operation.as_ref(), + cancellable.as_ref(), + callback, + ); +} + +unsafe extern "C" fn file_eject_mountable_with_operation_finish( + file: *mut ffi::GFile, + res: *mut ffi::GAsyncResult, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let result: &AsyncResult = &from_glib_borrow(res); + + let res_ = imp.eject_mountable_with_operation_finish(result); + match res_ { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_poll_mountable( + file: *mut ffi::GFile, + cancellable: *mut ffi::GCancellable, + callback: ffi::GAsyncReadyCallback, + user_data: glib::ffi::gpointer, +) { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + let callback = callback.map(|callback| { + move |source: &T::Type, res: &AsyncResult| unsafe { + callback( + source.upcast_ref::().to_glib_none().0, + res.to_glib_none().0, + user_data, + ) + } + }); + + imp.poll_mountable(cancellable.as_ref(), callback) +} + +unsafe extern "C" fn file_poll_mountable_finish( + file: *mut ffi::GFile, + res: *mut ffi::GAsyncResult, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let result: &AsyncResult = &from_glib_borrow(res); + + let res_ = imp.poll_mountable_finish(result); + match res_ { + Ok(_) => true.into_glib(), + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +unsafe extern "C" fn file_measure_disk_usage( + file: *mut ffi::GFile, + flags: ffi::GFileMeasureFlags, + cancellable: *mut ffi::GCancellable, + progress_callback: ffi::GFileMeasureProgressCallback, + progress_callback_data: glib::ffi::gpointer, + disk_usage: *mut u64, + num_dirs: *mut u64, + num_files: *mut u64, + error: *mut *mut glib::ffi::GError, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + let progress_callback = progress_callback.map(|callback| { + let boxed: Box = Box::new( + move |reporting: bool, current_size: u64, num_dirs: u64, num_files: u64| unsafe { + callback( + reporting.into_glib(), + current_size, + num_dirs, + num_files, + progress_callback_data, + ) + }, + ); + boxed + }); + + let res = imp.measure_disk_usage(from_glib(flags), cancellable.as_ref(), progress_callback); + match res { + Ok((disk_usage_, num_dirs_, num_files_)) => { + if !disk_usage.is_null() { + *disk_usage = disk_usage_ + } + if !num_dirs.is_null() { + *num_dirs = num_dirs_ + } + if !num_files.is_null() { + *num_files = num_files_ + } + true.into_glib() + } + Err(err) => { + if !error.is_null() { + *error = err.to_glib_full() + } + false.into_glib() + } + } +} + +#[cfg(feature = "v2_84")] +unsafe extern "C" fn file_query_exists( + file: *mut ffi::GFile, + cancellable: *mut ffi::GCancellable, +) -> glib::ffi::gboolean { + let instance = &*(file as *mut T::Instance); + let imp = instance.imp(); + let cancellable = Option::::from_glib_none(cancellable); + + let res = imp.query_exists(cancellable.as_ref()); + res.into_glib() +} + +#[cfg(test)] +mod tests; diff --git a/gio/src/subclass/file/tests.rs b/gio/src/subclass/file/tests.rs new file mode 100644 index 000000000000..f6059c0e4627 --- /dev/null +++ b/gio/src/subclass/file/tests.rs @@ -0,0 +1,3240 @@ +// Take a look at the license at the top of the repository in the LICENSE file. +// +// The following tests rely on a custom type `MyCustomFile` that extends the existing GIO type `MyFile`. Both types implement the interface `gio::auto::File`. +// For each virtual method defined in interface `crate::ffi::GFileIface`, a test checks that `MyCustomFile` and `MyFile` return the same results. + +use std::path::{Path, PathBuf}; + +use futures_channel::oneshot; + +use super::*; +use crate::{prelude::*, FileMonitorEvent, FileType}; + +mod imp { + use super::*; + use crate::{subclass::prelude::*, FileAttributeInfoFlags, FileAttributeType}; + use glib::ValueDelegate; + use std::{ + cell::{Cell, RefCell}, + hash::{self, Hash, Hasher}, + }; + + const SCHEME: &str = "myfile"; + + // Define custom types to use for properties in `MyFile`. + #[derive(Copy, Clone, Debug, PartialEq, ValueDelegate)] + #[value_delegate(from = ffi::GFileType)] + pub struct MyFileType(pub FileType); + + impl Default for MyFileType { + fn default() -> Self { + Self(FileType::Unknown) + } + } + + impl From for MyFileType { + fn from(v: ffi::GFileType) -> Self { + Self(unsafe { FileType::from_glib(v) }) + } + } + + impl<'a> From<&'a MyFileType> for ffi::GFileType { + fn from(v: &'a MyFileType) -> Self { + v.0.into_glib() + } + } + + impl From for ffi::GFileType { + fn from(v: MyFileType) -> Self { + From::from(&v) + } + } + + #[derive(Default, Copy, Clone, Debug, PartialEq, ValueDelegate)] + #[value_delegate(from = u8)] + pub enum MyFileState { + #[default] + DoesNotExist, + Exist, + Deleted, + Trashed, + } + + impl From for MyFileState { + fn from(v: u8) -> Self { + match v { + 1 => Self::Exist, + 2 => Self::Deleted, + 3 => Self::Trashed, + _ => Self::DoesNotExist, + } + } + } + + impl<'a> From<&'a MyFileState> for u8 { + fn from(v: &'a MyFileState) -> Self { + match v { + MyFileState::DoesNotExist => 0, + MyFileState::Exist => 1, + MyFileState::Deleted => 2, + MyFileState::Trashed => 3, + } + } + } + + impl From for u8 { + fn from(v: MyFileState) -> Self { + From::from(&v) + } + } + + // Define `MyFile` as a subclass of `File`. + #[derive(glib::Properties, Default, Debug)] + #[properties(wrapper_type = super::MyFile)] + pub struct MyFile { + #[property(construct_only)] + path: RefCell, + #[property(construct_only)] + xattrs: RefCell>, + #[property(construct_only)] + children: RefCell>, + #[property(construct_only)] + type_: Cell, + #[property(construct_only)] + state: Cell, + } + + #[glib::object_subclass] + impl ObjectSubclass for MyFile { + const NAME: &'static str = "MyFile"; + type Type = super::MyFile; + type Interfaces = (File,); + } + + #[glib::derived_properties] + impl ObjectImpl for MyFile {} + + // Implements `FileImpl` with custom implementation. + impl FileImpl for MyFile { + const SUPPORT_THREAD_CONTEXT: bool = true; + + fn dup(&self) -> File { + Self::Type::new(self.path.borrow().clone()).upcast() + } + + fn hash(&self) -> u32 { + let mut hasher = hash::DefaultHasher::new(); + self.path.borrow().hash(&mut hasher); + hasher.finish() as u32 + } + + fn equal(&self, file2: &File) -> bool { + match file2.downcast_ref::() { + Some(file2) => self.path == file2.imp().path, + None => false, + } + } + + fn is_native(&self) -> bool { + false + } + + fn has_uri_scheme(&self, uri_scheme: &str) -> bool { + uri_scheme == SCHEME + } + + fn uri_scheme(&self) -> Option { + Some(SCHEME.to_owned()) + } + + fn basename(&self) -> Option { + self.path.borrow().file_name().map(PathBuf::from) + } + + fn path(&self) -> Option { + Some(self.path.borrow().to_path_buf()) + } + + fn uri(&self) -> String { + format!("{}://{}", SCHEME, self.path.borrow().to_string_lossy()) + } + + fn parse_name(&self) -> String { + self.uri() + } + + fn parent(&self) -> Option { + self.path + .borrow() + .parent() + .map(|parent| Self::Type::new(parent).upcast()) + } + + fn has_prefix(&self, prefix: &File) -> bool { + if let Some(prefix_path) = prefix.path() { + self.path.borrow().starts_with(prefix_path) + } else { + false + } + } + + fn relative_path(&self, descendant: &File) -> Option { + match descendant.downcast_ref::() { + Some(descendant) => descendant + .imp() + .path + .borrow() + .as_path() + .strip_prefix(self.path.borrow().as_path()) + .ok() + .map(PathBuf::from), + None => None, + } + } + + fn resolve_relative_path(&self, relative_path: impl AsRef) -> File { + let relative_pathbuf = PathBuf::from(relative_path.as_ref()); + let path = if relative_pathbuf.is_absolute() { + relative_pathbuf + } else { + self.path.borrow().join(relative_pathbuf) + }; + Self::Type::new(path).upcast() + } + + fn child_for_display_name(&self, display_name: &str) -> Result { + let path = self.path.borrow().join(display_name); + Ok(Self::Type::new(path).upcast()) + } + + fn enumerate_children( + &self, + _attributes: &str, + _flags: FileQueryInfoFlags, + _cancellable: Option<&impl IsA>, + ) -> Result { + if self.type_.get().0 != FileType::Directory { + Err(Error::new( + IOErrorEnum::NotDirectory, + "File is not a directory", + )) + } else if self.state.get() != MyFileState::Exist { + Err(Error::new(IOErrorEnum::NotFound, "File does not exist")) + } else { + let enumerator = + super::MyFileEnumerator::new(self.children.borrow().clone()).upcast(); + Ok(enumerator) + } + } + + fn query_info( + &self, + attributes: &str, + _flags: FileQueryInfoFlags, + _cancellable: Option<&impl IsA>, + ) -> Result { + let file_info = FileInfo::new(); + let (mut name, mut xattr) = (false, Vec::new()); + for attribute in attributes.split(",") { + let (n, x) = match attribute { + "*" => (true, "*"), + "standard::*" | "standard::name" => (true, ""), + attribute if attribute.starts_with("xattr::") => (false, attribute), + _ => { + return Err(Error::new( + IOErrorEnum::InvalidArgument, + &format!("Querying attributes {attribute} not supported for MyFile"), + )) + } + }; + name |= n; + xattr.push(x); + } + if name { + file_info.set_name( + self.path + .borrow() + .file_name() + .map_or("none", |s| s.to_str().unwrap_or("none")), + ); + } + if !xattr.is_empty() { + let all = xattr.contains(&"xattr::*"); + for xattr in self.xattrs.borrow().iter() { + let (key, value) = xattr.split_once('=').unwrap_or((xattr, "")); + if all || xattr.contains(key) { + file_info.set_attribute(key, value); + } + } + } + Ok(file_info) + } + + fn query_filesystem_info( + &self, + attributes: &str, + cancellable: Option<&impl IsA>, + ) -> Result { + self.query_info(attributes, FileQueryInfoFlags::NONE, cancellable) + } + + fn find_enclosing_mount( + &self, + _cancellable: Option<&impl IsA>, + ) -> Result { + Err(Error::new( + IOErrorEnum::NotSupported, + "Find enclosing mount not supported for MyFile", + )) + } + + fn set_display_name( + &self, + display_name: &str, + _query_writable_namespaces_async: Option<&impl IsA>, + ) -> Result { + let path = match self.path.borrow().parent() { + Some(parent) => parent.join(display_name), + None => PathBuf::from(display_name), + }; + Ok(Self::Type::new(path).upcast()) + } + + fn query_settable_attributes( + &self, + _cancellable: Option<&impl IsA>, + ) -> Result { + let file_attribute_info_list = FileAttributeInfoList::new(); + file_attribute_info_list.add( + "standard::name", + FileAttributeType::String, + FileAttributeInfoFlags::NONE, + ); + for xattr in self.xattrs.borrow().iter() { + let (key, _value) = xattr.split_once('=').unwrap_or((xattr, "")); + file_attribute_info_list.add( + &format!("xattr::{key}"), + FileAttributeType::String, + FileAttributeInfoFlags::NONE, + ); + } + Ok(file_attribute_info_list) + } + + fn query_writable_namespaces( + &self, + _cancellable: Option<&impl IsA>, + ) -> Result { + let file_attribute_info_list = FileAttributeInfoList::new(); + file_attribute_info_list.add( + "xattr", + FileAttributeType::String, + FileAttributeInfoFlags::NONE, + ); + Ok(file_attribute_info_list) + } + + fn set_attribute<'a>( + &self, + attribute: &str, + value: impl Into>, + _flags: FileQueryInfoFlags, + _cancellable: Option<&impl IsA>, + ) -> Result<(), Error> { + let value = value.into(); + match (attribute, value.type_(), value.as_ptr()) { + ("standard::name", FileAttributeType::String, p) => { + let name = unsafe { + >::from_glib_none(p as *mut _) + }; + self.path.borrow_mut().set_file_name(name); + Ok(()) + } + (attribute, FileAttributeType::String, p) if attribute.starts_with("xattr:") => { + let value = unsafe { + >::from_glib_none(p as *mut _) + }; + self.xattrs + .borrow_mut() + .push(format!("{attribute}={value}")); + Ok(()) + } + (attribute, type_, _) => Err(Error::new( + IOErrorEnum::InvalidArgument, + &format!( + "Setting attribute '{attribute}' ({type_:?}) not supported for MyFile" + ), + )), + } + } + + fn set_attributes_from_info( + &self, + info: &FileInfo, + flags: FileQueryInfoFlags, + cancellable: Option<&impl IsA>, + ) -> Result<(), Error> { + for attribute in info.list_attributes(None) { + let value = info.attribute_as_string(attribute.as_str()).unwrap(); + self.set_attribute(attribute.as_str(), value.as_str(), flags, cancellable)?; + } + Ok(()) + } + + fn read_fn( + &self, + _cancellable: Option<&impl IsA>, + ) -> Result { + Err(Error::new( + IOErrorEnum::NotSupported, + "Reading not supported for MyFile", + )) + } + + fn append_to( + &self, + _flags: FileCreateFlags, + _cancellable: Option<&impl IsA>, + ) -> Result { + Err(Error::new( + IOErrorEnum::NotSupported, + "Appending to file not supported for MyFile", + )) + } + + fn create( + &self, + _flags: FileCreateFlags, + _cancellable: Option<&impl IsA>, + ) -> Result { + Err(Error::new( + IOErrorEnum::NotSupported, + "Creating file not supported for MyFile", + )) + } + + fn replace( + &self, + _etag: Option<&str>, + _make_backup: bool, + _flags: FileCreateFlags, + _cancellable: Option<&impl IsA>, + ) -> Result { + Err(Error::new( + IOErrorEnum::NotSupported, + "Replacing file not supported for MyFile", + )) + } + + fn delete(&self, _cancellable: Option<&impl IsA>) -> Result<(), Error> { + if self.state.get() != MyFileState::Exist { + Err(Error::new(IOErrorEnum::NotFound, "File does not exist")) + } else { + self.state.set(MyFileState::Deleted); + Ok(()) + } + } + + fn trash(&self, _cancellable: Option<&impl IsA>) -> Result<(), Error> { + if self.state.get() != MyFileState::Exist { + Err(Error::new(IOErrorEnum::NotFound, "File does not exist")) + } else { + self.state.set(MyFileState::Trashed); + Ok(()) + } + } + + fn make_directory( + &self, + _cancellable: Option<&impl IsA>, + ) -> Result<(), Error> { + if self.state.get() == MyFileState::Exist { + Err(Error::new(IOErrorEnum::Exists, "File already exists")) + } else { + self.state.set(MyFileState::Exist); + self.type_.set(MyFileType(FileType::Directory)); + Ok(()) + } + } + + fn make_symbolic_link( + &self, + _symlink_value: impl AsRef, + _cancellable: Option<&impl IsA>, + ) -> Result<(), Error> { + if self.state.get() == MyFileState::Exist { + Err(Error::new(IOErrorEnum::Exists, "File already exists")) + } else { + self.state.set(MyFileState::Exist); + self.type_.set(MyFileType(FileType::SymbolicLink)); + Ok(()) + } + } + + fn copy( + source: &File, + destination: &File, + flags: FileCopyFlags, + cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(), Error> { + let source_exists = source.downcast_ref::().map_or_else( + || source.query_exists(cancellable), + |f| f.imp().state.get() == MyFileState::Exist, + ); + let source_type = source.downcast_ref::().map_or_else( + || source.query_file_type(FileQueryInfoFlags::NONE, cancellable), + |f| f.imp().type_.get().0, + ); + let destination_exists = destination.downcast_ref::().map_or_else( + || destination.query_exists(cancellable), + |f| f.imp().state.get() == MyFileState::Exist, + ); + let destination_type = destination.downcast_ref::().map_or_else( + || destination.query_file_type(FileQueryInfoFlags::NONE, cancellable), + |f| f.imp().type_.get().0, + ); + let overwrite = flags.contains(FileCopyFlags::OVERWRITE); + match ( + source_exists, + source_type, + destination_exists, + destination_type, + overwrite, + ) { + (false, _, _, _, _) => { + Err(Error::new(IOErrorEnum::NotFound, "Source does not exist")) + } + (true, _, true, _, false) => Err(Error::new( + IOErrorEnum::Exists, + "Destination already exists", + )), + (true, FileType::Regular, true, FileType::Directory, true) => Err(Error::new( + IOErrorEnum::IsDirectory, + "Cannot overwrite a directory with a file", + )), + (true, FileType::Directory, true, FileType::Directory, true) => Err(Error::new( + IOErrorEnum::WouldMerge, + "Cannot overwrite a directory with a directory", + )), + (true, FileType::Directory, false, _, _) => Err(Error::new( + IOErrorEnum::WouldRecurse, + "Cannot handle recursive copy of source directory", + )), + (true, FileType::Directory, true, FileType::Regular, true) => Err(Error::new( + IOErrorEnum::WouldRecurse, + "Cannot handle recursive copy of source directory", + )), + _ => { + // Simulate a copy operation + if let Some(mut callback) = progress_callback { + for i in 0..10 { + // Simulate progress + callback(i * 10_i64, 100); + } + } + if let Some(dest) = destination.downcast_ref::() { + dest.imp().state.set(MyFileState::Exist); + dest.imp().type_.set(MyFileType(source_type)); + } + Ok(()) + } + } + } + + fn move_( + source: &File, + destination: &File, + flags: FileCopyFlags, + cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(), Error> { + let source_exists = source.downcast_ref::().map_or_else( + || source.query_exists(cancellable), + |f| f.imp().state.get() == MyFileState::Exist, + ); + let source_type = source.downcast_ref::().map_or_else( + || source.query_file_type(FileQueryInfoFlags::NONE, cancellable), + |f| f.imp().type_.get().0, + ); + let destination_exists = destination.downcast_ref::().map_or_else( + || destination.query_exists(cancellable), + |f| f.imp().state.get() == MyFileState::Exist, + ); + let destination_type = destination.downcast_ref::().map_or_else( + || destination.query_file_type(FileQueryInfoFlags::NONE, cancellable), + |f| f.imp().type_.get().0, + ); + let overwrite = flags.contains(FileCopyFlags::OVERWRITE); + match ( + source_exists, + source_type, + destination_exists, + destination_type, + overwrite, + ) { + (false, _, _, _, _) => { + Err(Error::new(IOErrorEnum::NotFound, "Source does not exist")) + } + (true, _, true, _, false) => Err(Error::new( + IOErrorEnum::Exists, + "Destination already exists", + )), + (true, FileType::Regular, true, FileType::Directory, true) => Err(Error::new( + IOErrorEnum::IsDirectory, + "Cannot overwrite a directory with a file", + )), + (true, FileType::Directory, true, FileType::Directory, true) => Err(Error::new( + IOErrorEnum::WouldMerge, + "Cannot overwrite a directory with a directory", + )), + (true, FileType::Directory, false, _, _) => Err(Error::new( + IOErrorEnum::WouldRecurse, + "Cannot handle recursive copy of source directory", + )), + (true, FileType::Directory, true, FileType::Regular, true) => Err(Error::new( + IOErrorEnum::WouldRecurse, + "Cannot handle recursive copy of source directory", + )), + _ => { + // Simulate a move operation + if let Some(mut callback) = progress_callback { + for i in 0..10 { + // Simulate progress + callback(i * 10_i64, 100); + } + } + if let Some(dest) = destination.downcast_ref::() { + dest.imp().state.set(MyFileState::Exist); + dest.imp().type_.set(MyFileType(source_type)); + } + Ok(()) + } + } + } + + fn mount_mountable( + &self, + _flags: MountMountFlags, + _mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let callback = callback.expect("callback is required"); + let task = unsafe { + crate::LocalTask::new( + Some(self.obj().as_ref()), + cancellable, + move |task: crate::LocalTask, source: Option<&Self::Type>| { + callback(source.unwrap(), task.upcast_ref::()) + }, + ) + }; + task.return_result(Err(Error::new( + IOErrorEnum::NotSupported, + "Mounting mountable not supported for MyFile", + ))); + } + + fn mount_mountable_finish(&self, res: &AsyncResult) -> Result { + unsafe { + res.downcast_ref::>() + .expect("res expected to be a LocalTask") + .to_owned() + .propagate() + } + } + + fn unmount_mountable( + &self, + _flags: MountUnmountFlags, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let callback = callback.expect("callback is required"); + let task = unsafe { + crate::LocalTask::new( + Some(self.obj().as_ref()), + cancellable, + move |task: crate::LocalTask, source: Option<&Self::Type>| { + callback(source.unwrap(), task.upcast_ref::()) + }, + ) + }; + task.return_result(Err(Error::new( + IOErrorEnum::NotSupported, + "Unmounting mountable not supported for MyFile", + ))); + } + + fn unmount_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + res.downcast_ref::>() + .expect("res expected to be a LocalTask") + .to_owned() + .propagate() + .map(|_| ()) + } + } + + fn eject_mountable( + &self, + _flags: MountUnmountFlags, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let callback = callback.expect("callback is required"); + let task = unsafe { + crate::LocalTask::new( + Some(self.obj().as_ref()), + cancellable, + move |task: crate::LocalTask, source: Option<&Self::Type>| { + callback(source.unwrap(), task.upcast_ref::()) + }, + ) + }; + task.return_result(Err(Error::new( + IOErrorEnum::NotSupported, + "Ejecting mountable not supported for MyFile", + ))); + } + + fn eject_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + res.downcast_ref::>() + .expect("res expected to be a LocalTask") + .to_owned() + .propagate() + .map(|_| ()) + } + } + + fn mount_enclosing_volume( + &self, + _flags: MountMountFlags, + _mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let callback = callback.expect("callback is required"); + let task = unsafe { + crate::LocalTask::new( + Some(self.obj().as_ref()), + cancellable, + move |task: crate::LocalTask, source: Option<&Self::Type>| { + callback(source.unwrap(), task.upcast_ref::()) + }, + ) + }; + task.return_result(Err(Error::new( + IOErrorEnum::NotSupported, + "Mounting enclosing volume not supported for MyFile", + ))); + } + + fn mount_enclosing_volume_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + res.downcast_ref::>() + .expect("res expected to be a LocalTask") + .to_owned() + .propagate() + .map(|_| ()) + } + } + + fn monitor_dir( + &self, + _flags: FileMonitorFlags, + _cancellable: Option<&impl IsA>, + ) -> Result { + if self.type_.get().0 != FileType::Directory { + Err(Error::new( + IOErrorEnum::NotDirectory, + "File is not a directory", + )) + } else if self.state.get() != MyFileState::Exist { + Err(Error::new(IOErrorEnum::NotFound, "File does not exist")) + } else { + let monitor = super::MyFileMonitor::new().upcast(); + Ok(monitor) + } + } + + fn monitor_file( + &self, + _flags: FileMonitorFlags, + _cancellable: Option<&impl IsA>, + ) -> Result { + if self.type_.get().0 != FileType::Regular { + Err(Error::new( + IOErrorEnum::NotRegularFile, + "File is not a file", + )) + } else if self.state.get() != MyFileState::Exist { + Err(Error::new(IOErrorEnum::NotFound, "File does not exist")) + } else { + let monitor = super::MyFileMonitor::new().upcast(); + Ok(monitor) + } + } + + fn open_readwrite( + &self, + _cancellable: Option<&impl IsA>, + ) -> Result { + Err(Error::new( + IOErrorEnum::NotSupported, + "Opening file for read/write not supported for MyFile", + )) + } + + fn create_readwrite( + &self, + _flags: FileCreateFlags, + _cancellable: Option<&impl IsA>, + ) -> Result { + Err(Error::new( + IOErrorEnum::NotSupported, + "Creating file for read/write not supported for MyFile", + )) + } + + fn replace_readwrite( + &self, + _etag: Option<&str>, + _make_backup: bool, + _flags: FileCreateFlags, + _cancellable: Option<&impl IsA>, + ) -> Result { + Err(Error::new( + IOErrorEnum::NotSupported, + "Replacing file for read/write not supported for MyFile", + )) + } + + fn start_mountable( + &self, + _flags: DriveStartFlags, + _mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let callback = callback.expect("callback is required"); + let task = unsafe { + crate::LocalTask::new( + Some(self.obj().as_ref()), + cancellable, + move |task: crate::LocalTask, source: Option<&Self::Type>| { + callback(source.unwrap(), task.upcast_ref::()) + }, + ) + }; + task.return_result(Err(Error::new( + IOErrorEnum::NotSupported, + "Starting mountable not supported for MyFile", + ))); + } + + fn start_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + res.downcast_ref::>() + .expect("res expected to be a LocalTask") + .to_owned() + .propagate() + .map(|_| ()) + } + } + + fn stop_mountable( + &self, + _flags: MountUnmountFlags, + _mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let callback = callback.expect("callback is required"); + let task = unsafe { + crate::LocalTask::new( + Some(self.obj().as_ref()), + cancellable, + move |task: crate::LocalTask, source: Option<&Self::Type>| { + callback(source.unwrap(), task.upcast_ref::()) + }, + ) + }; + task.return_result(Err(Error::new( + IOErrorEnum::NotSupported, + "Stopping mountable not supported for MyFile", + ))); + } + + fn stop_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + res.downcast_ref::>() + .expect("res expected to be a LocalTask") + .to_owned() + .propagate() + .map(|_| ()) + } + } + + fn unmount_mountable_with_operation( + &self, + _flags: MountUnmountFlags, + _mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let callback = callback.expect("callback is required"); + let task = unsafe { + crate::LocalTask::new( + Some(self.obj().as_ref()), + cancellable, + move |task: crate::LocalTask, source: Option<&Self::Type>| { + callback(source.unwrap(), task.upcast_ref::()) + }, + ) + }; + task.return_result(Err(Error::new( + IOErrorEnum::NotSupported, + "Unmounting mountable with operation not supported for MyFile", + ))); + } + + fn unmount_mountable_with_operation_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + res.downcast_ref::>() + .expect("res expected to be a LocalTask") + .to_owned() + .propagate() + .map(|_| ()) + } + } + + fn eject_mountable_with_operation( + &self, + _flags: MountUnmountFlags, + _mount_operation: Option<&MountOperation>, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let callback = callback.expect("callback is required"); + let task = unsafe { + crate::LocalTask::new( + Some(self.obj().as_ref()), + cancellable, + move |task: crate::LocalTask, source: Option<&Self::Type>| { + callback(source.unwrap(), task.upcast_ref::()) + }, + ) + }; + task.return_result(Err(Error::new( + IOErrorEnum::NotSupported, + "Ejecting mountable with operation not supported for MyFile", + ))); + } + + fn eject_mountable_with_operation_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + res.downcast_ref::>() + .expect("res expected to be a LocalTask") + .to_owned() + .propagate() + .map(|_| ()) + } + } + + fn poll_mountable( + &self, + cancellable: Option<&impl IsA>, + callback: Option

, + ) { + let callback = callback.expect("callback is required"); + let task = unsafe { + crate::LocalTask::new( + Some(self.obj().as_ref()), + cancellable, + move |task: crate::LocalTask, source: Option<&Self::Type>| { + callback(source.unwrap(), task.upcast_ref::()) + }, + ) + }; + task.return_result(Err(Error::new( + IOErrorEnum::NotSupported, + "Polling mountable not supported for MyFile", + ))); + } + + fn poll_mountable_finish(&self, res: &AsyncResult) -> Result<(), Error> { + unsafe { + res.downcast_ref::>() + .expect("res expected to be a LocalTask") + .to_owned() + .propagate() + .map(|_| ()) + } + } + + fn measure_disk_usage( + &self, + _flags: FileMeasureFlags, + _cancellable: Option<&impl IsA>, + progress_callback: Option>, + ) -> Result<(u64, u64, u64), Error> { + // Simulate a measure disk operation + if let Some(mut callback) = progress_callback { + for i in 0..10u64 { + // Simulate progress + callback(true, i * 10, i, i * 5); + } + callback(false, 100u64, 10u64, 50u64); + } + Ok((100u64, 10u64, 50u64)) + } + + fn query_exists(&self, _cancellable: Option<&impl IsA>) -> bool { + true + } + } + + #[derive(Default)] + pub struct MyCustomFile; + + // Define `MyCustomFile` as a subclass of `MyFile`. + #[glib::object_subclass] + impl ObjectSubclass for MyCustomFile { + const NAME: &'static str = "MyCustomFile"; + type Type = super::MyCustomFile; + type ParentType = super::MyFile; + type Interfaces = (File,); + } + + impl ObjectImpl for MyCustomFile {} + + // Implements `FileImpl` with default implementation, which calls the parent's implementation. + impl FileImpl for MyCustomFile {} + + impl MyFileImpl for MyCustomFile {} + + pub(super) mod file_enumerator { + use super::*; + + // Define `MyFileEnumerator` as a subclass of `FileEnumerator`. + #[derive(glib::Properties, Default, Debug)] + #[properties(wrapper_type = super::MyFileEnumerator)] + pub struct MyFileEnumerator { + #[property(construct_only)] + children: RefCell>, + index: Cell, + } + + #[glib::object_subclass] + impl ObjectSubclass for MyFileEnumerator { + const NAME: &'static str = "MyFileMyEnumerator"; // Different name than `MyFileEnumerator` defined in `FileEnumerator` tests. + type Type = super::MyFileEnumerator; + type ParentType = FileEnumerator; + } + + #[glib::derived_properties] + impl ObjectImpl for MyFileEnumerator { + fn dispose(&self) { + let _ = self.obj().close(Cancellable::NONE); + } + } + + // Implements `FileEnumeratorImpl` with custom implementation. + impl FileEnumeratorImpl for MyFileEnumerator { + fn next_file( + &self, + _cancellable: Option<&Cancellable>, + ) -> Result, glib::Error> { + match self.index.get() { + -1 => Err(glib::Error::new::( + IOErrorEnum::Closed, + "Enumerator is closed", + )), + i if (i as usize) < self.children.borrow().len() => { + let file_info = FileInfo::new(); + file_info.set_name(self.children.borrow()[i as usize].as_str()); + self.index.set(i + 1); + Ok(Some(file_info)) + } + _ => Ok(None), + } + } + + fn close(&self, _cancellable: Option<&Cancellable>) -> (bool, Option) { + self.index.set(-1); + (true, None) + } + } + } + + pub(super) mod file_monitor { + use super::*; + + // Define `MyFileMonitor` as a subclass of `FileMonitor`. + #[derive(Default, Debug)] + pub struct MyFileMonitor; + + #[glib::object_subclass] + impl ObjectSubclass for MyFileMonitor { + const NAME: &'static str = "MyFileMyMonitor"; // Different name than `MyFileMonitor` defined in `FileMonitor` tests. + type Type = super::MyFileMonitor; + type ParentType = FileMonitor; + } + + impl ObjectImpl for MyFileMonitor {} + + // Implements `FileMonitorImpl` with custom implementation. + impl FileMonitorImpl for MyFileMonitor { + fn cancel(&self) {} + } + } +} + +use imp::{MyFileState, MyFileType}; + +glib::wrapper! { + pub struct MyFile(ObjectSubclass) @implements File; +} + +impl MyFile { + fn new>(path: P) -> Self { + Object::builder() + .property("path", path.as_ref().to_path_buf()) + .build() + } + + fn with_xattr<'a, P: AsRef, X: AsRef<[&'a str]>>(path: P, xattrs: X) -> Self { + Object::builder() + .property("path", path.as_ref().to_path_buf()) + .property("xattrs", xattrs.as_ref()) + .build() + } + + fn with_children<'a, P: AsRef, C: AsRef<[&'a str]>>(path: P, children: C) -> Self { + Object::builder() + .property("path", path.as_ref().to_path_buf()) + .property("children", children.as_ref()) + .property("type", MyFileType(FileType::Directory)) + .property("state", MyFileState::Exist) + .build() + } + + fn with_type_state>(path: P, type_: FileType, state: MyFileState) -> Self { + Object::builder() + .property("path", path.as_ref().to_path_buf()) + .property("type", MyFileType(type_)) + .property("state", state) + .build() + } +} + +pub trait MyFileImpl: ObjectImpl + ObjectSubclass + IsA> {} + +// To make this class subclassable we need to implement IsSubclassable +unsafe impl IsSubclassable for MyFile {} + +glib::wrapper! { + pub struct MyCustomFile(ObjectSubclass) @extends MyFile, @implements File; +} + +impl MyCustomFile { + fn new>(path: P) -> Self { + Object::builder() + .property("path", path.as_ref().to_path_buf()) + .build() + } + + fn with_xattr<'a, P: AsRef, X: AsRef<[&'a str]>>(path: P, xattrs: X) -> Self { + Object::builder() + .property("path", path.as_ref().to_path_buf()) + .property("xattrs", xattrs.as_ref()) + .build() + } + + fn with_children<'a, P: AsRef, C: AsRef<[&'a str]>>(path: P, children: C) -> Self { + Object::builder() + .property("path", path.as_ref().to_path_buf()) + .property("children", children.as_ref()) + .property("type", MyFileType(FileType::Directory)) + .property("state", MyFileState::Exist) + .build() + } + + fn with_type_state>(path: P, type_: FileType, state: MyFileState) -> Self { + Object::builder() + .property("path", path.as_ref().to_path_buf()) + .property("type", MyFileType(type_)) + .property("state", state) + .build() + } +} + +glib::wrapper! { + pub struct MyFileEnumerator(ObjectSubclass) @extends FileEnumerator; +} + +impl MyFileEnumerator { + pub fn new(children: Vec) -> Self { + Object::builder().property("children", children).build() + } +} + +glib::wrapper! { + pub struct MyFileMonitor(ObjectSubclass) @extends FileMonitor; +} + +impl MyFileMonitor { + pub fn new() -> Self { + Object::builder().build() + } +} + +impl Default for MyFileMonitor { + fn default() -> Self { + Self::new() + } +} + +#[test] +fn file_dup() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::dup` + let my_custom_file = MyCustomFile::new("/my_file"); + let dup = my_custom_file.dup(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::dup` + let my_file = MyFile::new("/my_file"); + let expected = my_file.dup(); + + // both results should equal + assert!(dup.equal(&expected)); +} + +// checker-ignore-item +#[test] +fn file_hash() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::hash` + let my_custom_file = MyCustomFile::new("/my_file"); + let hash = unsafe { + crate::ffi::g_file_hash( + , + >>::to_glib_none(&my_custom_file) + .0 as glib::ffi::gconstpointer, + ) + }; + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::hash` + let my_file = MyFile::new("/my_file"); + let expected = unsafe { + crate::ffi::g_file_hash( + , + >>::to_glib_none(&my_file) + .0 as glib::ffi::gconstpointer, + ) + }; + + // both hash values should equal + assert_eq!(hash, expected); +} + +#[test] +fn file_equal() { + // 2 instances of `MyCustomFile` with same path should equal + let my_custom_file = MyCustomFile::new("/my_file"); + let expected = MyCustomFile::new("/my_file"); + assert!(my_custom_file.equal(&expected)); + + // instances of `MyCustomFile` and of `MyFile` with same path should not equal (because type is different) + let expected = File::for_path(PathBuf::from("/my_file")); + assert!(!my_custom_file.equal(&expected)); +} + +#[test] +fn file_is_native() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::is_native` + let my_custom_file = MyCustomFile::new("/my_file"); + let is_native = my_custom_file.is_native(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::is_native` + let my_file = MyFile::new("/my_file"); + let expected = my_file.is_native(); + + // both results should equal + assert_eq!(is_native, expected); +} + +#[test] +fn file_has_uri_scheme() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::has_uri_scheme` + let my_custom_file = MyCustomFile::new("/my_file"); + let has_file = my_custom_file.has_uri_scheme("file"); + let has_foo = my_custom_file.has_uri_scheme("foo"); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::has_uri_scheme` + let my_file = MyFile::new("/my_file"); + + let expected_file = my_file.has_uri_scheme("file"); + let expected_foo = my_file.has_uri_scheme("foo"); + + // both results should equal + assert_eq!(has_file, expected_file); + assert_eq!(has_foo, expected_foo); +} + +#[test] +fn file_uri_scheme() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::uri_scheme` + let my_custom_file = MyCustomFile::new("/my_file"); + let uri_scheme = my_custom_file.uri_scheme(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::uri_scheme` + let my_file = MyFile::new("/my_file"); + let expected = my_file.uri_scheme(); + + // both uri schemes should equal + assert_eq!(uri_scheme, expected); +} + +#[test] +fn file_basename() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::basename` + let my_custom_file = MyCustomFile::new("/my_file"); + let basename = my_custom_file.basename(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::basename` + let my_file = MyFile::new("/my_file"); + let expected = my_file.basename(); + + // both basenames should equal + assert_eq!(basename, expected); +} + +#[test] +fn file_path() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::path` + let my_custom_file = MyCustomFile::new("/my_file"); + let path = my_custom_file.path(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::path` + let my_file = MyFile::new("/my_file"); + let expected = my_file.path(); + + // both paths should equal + assert_eq!(path, expected); +} + +#[test] +fn file_uri() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::uri` + let my_custom_file = MyCustomFile::new("/my_file"); + let uri = my_custom_file.uri(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::uri` + let my_file = MyFile::new("/my_file"); + let expected = my_file.uri(); + + // both uris should equal + assert_eq!(uri, expected); +} + +#[test] +fn file_parse_name() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::parse_name` + let my_custom_file = MyCustomFile::new("/my_file"); + let parse_name = my_custom_file.parse_name(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::parse_name` + let my_file = MyFile::new("/my_file"); + let expected = my_file.parse_name(); + + // both parse names should equal + assert_eq!(parse_name, expected); +} + +#[test] +fn file_parent() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::parent` + let my_custom_file = MyCustomFile::new("/my_parent/my_file"); + let res = my_custom_file.parent(); + assert!(res.is_some(), "unexpected None"); + let parent = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::parent` + let my_file = MyFile::new("/my_parent/my_file"); + let res = my_file.parent(); + assert!(res.is_some(), "unexpected None"); + let expected = res.unwrap(); + + // both parents should equal + assert!(parent.equal(&expected)); +} + +#[test] +fn file_has_prefix() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::has_prefix` + let my_custom_file = MyCustomFile::new("/my_prefix/my_file"); + let my_custom_prefix = MyCustomFile::new("/my_prefix"); + let has_prefix = my_custom_file.has_prefix(&my_custom_prefix); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::has_prefix` + let my_file = MyFile::new("/my_prefix/my_file"); + let my_prefix = MyFile::new("/my_prefix"); + let expected = my_file.has_prefix(&my_prefix); + + // both results should equal + assert_eq!(has_prefix, expected); +} + +#[test] +fn file_relative_path() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::relative_path` + let my_custom_parent = MyCustomFile::new("/my_parent"); + let my_custom_descendant = MyCustomFile::new("/my_parent/my_descendant"); + let relative_path = my_custom_parent.relative_path(&my_custom_descendant); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::relative_path` + let my_parent = MyFile::new("/my_parent"); + let my_descendant = MyFile::new("/my_parent/my_descendant"); + let expected = my_parent.relative_path(&my_descendant); + + // both relative paths should equal + assert_eq!(relative_path, expected); +} + +#[test] +fn file_resolve_relative_path() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::resolve_relative_path` + let my_custom_prefix = MyCustomFile::new("/my_prefix"); + let resolved_path = my_custom_prefix.resolve_relative_path("my_file"); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::resolve_relative_path` + let my_prefix = MyFile::new("/my_prefix"); + let expected = my_prefix.resolve_relative_path("my_file"); + + // both resolved path result should equal + assert!(resolved_path.equal(&expected)); +} + +#[test] +fn file_child_for_display_name() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::child_for_display_name` + let my_custom_parent = MyCustomFile::new("/my_parent"); + let res = my_custom_parent.child_for_display_name("my_file"); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let child = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::child_for_display_name` + let my_parent = MyFile::new("/my_parent"); + let res = my_parent.child_for_display_name("my_file"); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let expected = res.unwrap(); + + // both children should equal + assert!(child.equal(&expected)) +} + +#[test] +fn file_enumerate_children() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::enumerate_children` + let my_custom_file = + MyCustomFile::with_type_state("/my_file", FileType::Regular, MyFileState::Exist); + let res = my_custom_file.enumerate_children("*", FileQueryInfoFlags::NONE, Cancellable::NONE); + assert!(res.is_err(), "unexpected enumerator"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::enumerate_children` + let my_file = MyFile::with_type_state("/my_file", FileType::Regular, MyFileState::Exist); + let res = my_file.enumerate_children("*", FileQueryInfoFlags::NONE, Cancellable::NONE); + assert!(res.is_err(), "unexpected enumerator"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::enumerate_children` + let my_custom_parent = MyCustomFile::with_children("/my_parent", vec!["my_file1", "my_file2"]); + let res = my_custom_parent.enumerate_children("*", FileQueryInfoFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let my_custom_enumerator = res.unwrap(); + let res = my_custom_enumerator + .map(|res| res.map(|file_info| file_info.name())) + .collect::, _>>(); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let my_custom_children = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::enumerate_children` + let my_parent = MyFile::with_children("/my_parent", vec!["my_file1", "my_file2"]); + let res = my_parent.enumerate_children("*", FileQueryInfoFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let my_enumerator = res.unwrap(); + let res = my_enumerator + .map(|res| res.map(|file_info| file_info.name())) + .collect::, _>>(); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let expected = res.unwrap(); + + // both children should equal + assert_eq!(my_custom_children, expected) +} + +#[test] +fn file_query_info() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::query_info` + let my_custom_file = + MyCustomFile::with_xattr("/my_file", vec!["xattr::key1=value1", "xattr::key2=value2"]); + let res = my_custom_file.query_info("*", FileQueryInfoFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let file_info = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::query_info` + let my_file = MyFile::with_xattr("/my_file", vec!["xattr::key1=value1", "xattr::key2=value2"]); + let res = my_file.query_info("*", FileQueryInfoFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let expected = res.unwrap(); + + // both results should equal + assert_eq!(file_info.name(), expected.name()); + assert_eq!( + file_info.attribute_as_string("xattr::key1"), + expected.attribute_as_string("xattr::key1") + ); + assert_eq!( + file_info.attribute_as_string("xattr::key2"), + expected.attribute_as_string("xattr::key2") + ); +} + +#[test] +fn file_query_filesystem_info() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::query_filesystem_info` + let my_custom_file = + MyCustomFile::with_xattr("/my_file", vec!["xattr::key1=value1", "xattr::key2=value2"]); + let res = my_custom_file.query_filesystem_info("*", Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let file_info = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::query_filesystem_info` + let my_file = MyFile::with_xattr("/my_file", vec!["xattr::key1=value1", "xattr::key2=value2"]); + let res = my_file.query_filesystem_info("*", Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let expected = res.unwrap(); + + // both results should equal + assert_eq!(file_info.name(), expected.name()); + assert_eq!( + file_info.attribute_as_string("xattr::key1"), + expected.attribute_as_string("xattr::key1") + ); + assert_eq!( + file_info.attribute_as_string("xattr::key2"), + expected.attribute_as_string("xattr::key2") + ); +} + +#[test] +fn file_find_enclosing_mount() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::find_enclosing_mount` + let my_custom_dir = MyCustomFile::new("/my_directory"); + let res = my_custom_dir.find_enclosing_mount(Cancellable::NONE); + assert!(res.is_err(), "unexpected mount {:?}", res.ok().unwrap()); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::find_enclosing_mount` + let my_dir = MyFile::new("/my_directory"); + let res = my_dir.find_enclosing_mount(Cancellable::NONE); + assert!(res.is_err(), "unexpected mount {:?}", res.ok().unwrap()); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); +} + +#[test] +fn file_set_display_name() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::set_display_name` + let my_custom_file = MyCustomFile::new("/my_file"); + let res = my_custom_file.set_display_name("my_file_new_name", Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let renamed = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::set_display_name` + let my_file = MyFile::new("/my_file"); + let res = my_file.set_display_name("my_file_new_name", Cancellable::NONE); + let expected = res.unwrap(); + + // both new paths should equal + assert_eq!(renamed.path(), expected.path()); +} + +#[test] +fn file_query_settable_attributes() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::query_settable_attributes` + let my_custom_file = + MyCustomFile::with_xattr("/my_file", vec!["xattr::key1=value1", "xattr::key2=value2"]); + let res = my_custom_file.query_settable_attributes(Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let file_attribute_infos = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::query_settable_attributes` + let my_file = MyFile::with_xattr("/my_file", vec!["xattr::key1=value1", "xattr::key2=value2"]); + let res = my_file.query_settable_attributes(Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let expected = res.unwrap(); + + // both file attribute infos should equal + assert_eq!( + file_attribute_infos + .attributes() + .iter() + .map(|attr| attr.name().to_owned()) + .collect::>(), + expected + .attributes() + .iter() + .map(|attr| attr.name().to_owned()) + .collect::>() + ); +} + +#[test] +fn file_query_writable_namespaces() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::query_writable_namespaces` + let my_custom_file = MyCustomFile::new("/my_file"); + let res = my_custom_file.query_writable_namespaces(Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let file_attribute_infos = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::query_writable_namespaces` + let my_file = MyFile::new("/my_file"); + let res = my_file.query_writable_namespaces(Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let expected = res.unwrap(); + + // both file attribute infos should equal + assert_eq!( + file_attribute_infos + .attributes() + .iter() + .map(|attr| attr.name().to_owned()) + .collect::>(), + expected + .attributes() + .iter() + .map(|attr| attr.name().to_owned()) + .collect::>() + ); +} + +#[test] +fn file_set_attribute() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::set_attribute` + let my_custom_file = MyCustomFile::new("/my_file"); + let res = my_custom_file.set_attribute( + "xattr::key1", + "value1", + FileQueryInfoFlags::NONE, + Cancellable::NONE, + ); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let res = my_custom_file.query_info("xattr::key1", FileQueryInfoFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let file_info = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::set_attribute` + let my_file = MyFile::new("/my_file"); + let res = my_file.set_attribute( + "xattr::key1", + "value1", + FileQueryInfoFlags::NONE, + Cancellable::NONE, + ); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let res = my_file.query_info("xattr::key1", FileQueryInfoFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let expected = res.unwrap(); + + // both file attributes should equal + assert_eq!( + file_info.attribute_as_string("xattr::key1"), + expected.attribute_as_string("xattr::key1") + ); +} + +#[test] +fn file_set_attributes_from_info() { + let file_info = FileInfo::new(); + file_info.set_attribute_string("xattr::key1", "value1"); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::set_attributes_from_info` + let my_custom_file = MyCustomFile::new("/my_file"); + let res = my_custom_file.set_attributes_from_info( + &file_info, + FileQueryInfoFlags::NONE, + Cancellable::NONE, + ); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let res = my_custom_file.query_info("xattr::key1", FileQueryInfoFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let file_info = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::set_attributes_from_info` + let my_file = MyFile::new("/my_file"); + let res = + my_file.set_attributes_from_info(&file_info, FileQueryInfoFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let res = my_file.query_info("xattr::key1", FileQueryInfoFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let expected = res.unwrap(); + + // both file attributes should equal + assert_eq!( + file_info.attribute_as_string("xattr::key1"), + expected.attribute_as_string("xattr::key1") + ); +} + +#[test] +fn file_read_fn() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::read_fn` + let my_custom_file = MyCustomFile::new("/my_file"); + let res = my_custom_file.read(Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file input stream {:?}", + res.ok().unwrap() + ); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::read_fn` + let my_file = MyFile::new("/my_file"); + let res = my_file.read(Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file input stream {:?}", + res.ok().unwrap() + ); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); +} + +#[test] +fn file_append_to() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::append_to` + let my_custom_file = MyCustomFile::new("/my_file"); + let res = my_custom_file.append_to(FileCreateFlags::NONE, Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file output stream {:?}", + res.ok().unwrap() + ); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::append_to` + let my_file = MyFile::new("/my_file"); + let res = my_file.append_to(FileCreateFlags::NONE, Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file output stream {:?}", + res.ok().unwrap() + ); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); +} + +#[test] +fn file_create() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::create` + let my_custom_file = MyCustomFile::new("/my_file"); + let res = my_custom_file.create(FileCreateFlags::NONE, Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file output stream {:?}", + res.ok().unwrap() + ); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::create` + let my_file = MyFile::new("/my_file"); + let res = my_file.create(FileCreateFlags::NONE, Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file output stream {:?}", + res.ok().unwrap() + ); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); +} + +#[test] +fn file_replace() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::replace` + let my_custom_file = MyCustomFile::new("/my_file"); + let res = my_custom_file.replace(None, false, FileCreateFlags::NONE, Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file output stream {:?}", + res.ok().unwrap() + ); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::replace` + let my_file = MyFile::new("/my_file"); + let res = my_file.replace(None, false, FileCreateFlags::NONE, Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file output stream {:?}", + res.ok().unwrap() + ); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); +} + +#[test] +fn file_delete() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::delete` + let my_custom_file = + MyCustomFile::with_type_state("/my_file", FileType::Unknown, MyFileState::Exist); + let res = my_custom_file.delete(Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::delete` + let my_file = MyFile::with_type_state("/my_file", FileType::Unknown, MyFileState::Exist); + let res = my_file.delete(Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::delete` + let res = my_custom_file.delete(Cancellable::NONE); + assert!(res.is_err(), "unexpected deleted file"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::delete` + let res = my_custom_file.delete(Cancellable::NONE); + assert!(res.is_err(), "unexpected deleted file"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); +} + +#[test] +fn file_trash() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::trash` + let my_custom_file = + MyCustomFile::with_type_state("/my_file", FileType::Unknown, MyFileState::Exist); + let res = my_custom_file.trash(Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::trash` + let my_file = MyFile::with_type_state("/my_file", FileType::Unknown, MyFileState::Exist); + let res = my_file.trash(Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::trash` + let res = my_custom_file.trash(Cancellable::NONE); + assert!(res.is_err(), "unexpected trashed file"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::trash` + let res = my_custom_file.trash(Cancellable::NONE); + assert!(res.is_err(), "unexpected trashed file"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); +} + +#[test] +fn file_make_directory() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::make_directory` + let my_custom_dir = MyCustomFile::new("/my_directory"); + let res = my_custom_dir.make_directory(Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::make_directory` + let my_dir = MyFile::new("/my_directory"); + let res = my_dir.make_directory(Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::make_directory` + let res = my_custom_dir.make_directory(Cancellable::NONE); + assert!(res.is_err(), "unexpected created directory"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::make_directory` + let res = my_custom_dir.make_directory(Cancellable::NONE); + assert!(res.is_err(), "unexpected created directory"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); +} + +#[test] +fn file_make_symbolic_link() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::make_symbolic_link` + let my_custom_symlink = MyCustomFile::new("/my_symbolic_link"); + let res = my_custom_symlink.make_symbolic_link("/my_target", Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::make_symbolic_link` + let my_symlink = MyFile::new("/my_symbolic_link"); + let res = my_symlink.make_symbolic_link("/my_target", Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::make_symbolic_link` + let res = my_custom_symlink.make_symbolic_link("/my_target", Cancellable::NONE); + assert!(res.is_err(), "unexpected created directory"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::make_symbolic_link` + let res = my_custom_symlink.make_symbolic_link("/my_target", Cancellable::NONE); + assert!(res.is_err(), "unexpected created directory"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); +} + +#[test] +fn file_copy() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::copy` + let my_custom_source_file = MyCustomFile::new("/my_file1"); + let my_custom_destination_file = MyCustomFile::new("/my_file2"); + let res = my_custom_source_file.copy( + &my_custom_destination_file, + FileCopyFlags::NONE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected copy"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::copy` + let my_source_file = MyFile::new("/my_file1"); + let my_destination_file = MyFile::new("/my_file2"); + let res = my_source_file.copy( + &my_destination_file, + FileCopyFlags::NONE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected copy"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + assert_eq!(err.kind::(), Some(IOErrorEnum::NotFound)); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::copy` + let my_custom_source_file = + MyCustomFile::with_type_state("/my_file1", FileType::Regular, MyFileState::Exist); + let my_custom_destination_file = + MyCustomFile::with_type_state("/my_file2", FileType::Regular, MyFileState::Exist); + let res = my_custom_source_file.copy( + &my_custom_destination_file, + FileCopyFlags::NONE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected copy"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::copy` + let my_source_file = + MyFile::with_type_state("/my_file1", FileType::Regular, MyFileState::Exist); + let my_destination_file = + MyFile::with_type_state("/my_file2", FileType::Regular, MyFileState::Exist); + let res = my_source_file.copy( + &my_destination_file, + FileCopyFlags::NONE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected copy"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + assert_eq!(err.kind::(), Some(IOErrorEnum::Exists)); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::copy` + let my_custom_source_file = + MyCustomFile::with_type_state("/my_file1", FileType::Regular, MyFileState::Exist); + let my_custom_destination_directory = + MyCustomFile::with_type_state("/my_dir2", FileType::Directory, MyFileState::Exist); + let res = my_custom_source_file.copy( + &my_custom_destination_directory, + FileCopyFlags::OVERWRITE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected copy"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::copy` + let my_source_file = + MyFile::with_type_state("/my_file1", FileType::Regular, MyFileState::Exist); + let my_destination_directory = + MyFile::with_type_state("/my_dir2", FileType::Directory, MyFileState::Exist); + let res = my_source_file.copy( + &my_destination_directory, + FileCopyFlags::OVERWRITE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected copy"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + assert_eq!(err.kind::(), Some(IOErrorEnum::IsDirectory)); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::copy` + let my_custom_source_directory = + MyCustomFile::with_type_state("/my_dir1", FileType::Directory, MyFileState::Exist); + let my_custom_destination_directory = + MyCustomFile::with_type_state("/my_dir2", FileType::Directory, MyFileState::Exist); + let res = my_custom_source_directory.copy( + &my_custom_destination_directory, + FileCopyFlags::OVERWRITE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected copy"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::copy` + let my_source_directory = + MyFile::with_type_state("/my_dir1", FileType::Directory, MyFileState::Exist); + let my_destination_directory = + MyFile::with_type_state("/my_dir2", FileType::Directory, MyFileState::Exist); + let res = my_source_directory.copy( + &my_destination_directory, + FileCopyFlags::OVERWRITE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected copy"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + assert_eq!(err.kind::(), Some(IOErrorEnum::WouldMerge)); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::copy` + let my_custom_source_directory = + MyCustomFile::with_type_state("/my_dir1", FileType::Directory, MyFileState::Exist); + let my_custom_destination_directory = + MyCustomFile::with_type_state("/my_dir2", FileType::Directory, MyFileState::DoesNotExist); + let res = my_custom_source_directory.copy( + &my_custom_destination_directory, + FileCopyFlags::NONE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected copy"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::copy` + let my_source_directory = + MyFile::with_type_state("/my_dir1", FileType::Directory, MyFileState::Exist); + let my_destination_directory = + MyFile::with_type_state("/my_dir2", FileType::Directory, MyFileState::DoesNotExist); + let res = my_source_directory.copy( + &my_destination_directory, + FileCopyFlags::NONE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected copy"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + assert_eq!(err.kind::(), Some(IOErrorEnum::WouldRecurse)); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::copy` + let my_custom_source_directory = + MyCustomFile::with_type_state("/my_dir1", FileType::Directory, MyFileState::Exist); + let my_custom_destination_file = + MyCustomFile::with_type_state("/my_file2", FileType::Regular, MyFileState::Exist); + let res = my_custom_source_directory.copy( + &my_custom_destination_file, + FileCopyFlags::OVERWRITE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected copy"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::copy` + let my_source_directory = + MyFile::with_type_state("/my_dir1", FileType::Directory, MyFileState::Exist); + let my_destination_file = + MyFile::with_type_state("/my_file2", FileType::Regular, MyFileState::Exist); + let res = my_source_directory.copy( + &my_destination_file, + FileCopyFlags::OVERWRITE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected copy"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + assert_eq!(err.kind::(), Some(IOErrorEnum::WouldRecurse)); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::copy` + let my_custom_source_file = + MyCustomFile::with_type_state("/my_file1", FileType::Regular, MyFileState::Exist); + let my_custom_destination_file = + MyCustomFile::with_type_state("/my_file2", FileType::Regular, MyFileState::DoesNotExist); + let (mut copied, mut total) = (0, 0); + let res = my_custom_source_file.copy( + &my_custom_destination_file, + FileCopyFlags::NONE, + Cancellable::NONE, + Some(Box::new(move |current_num_bytes, total_num_bytes| { + assert!(current_num_bytes >= copied); + assert!(total_num_bytes >= total); + copied = current_num_bytes; + total = total_num_bytes; + })), + ); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::copy` + let my_source_file = + MyFile::with_type_state("/my_file1", FileType::Regular, MyFileState::Exist); + let my_destination_file = + MyFile::with_type_state("/my_file2", FileType::Regular, MyFileState::DoesNotExist); + let (mut copied, mut total) = (0, 0); + let expected = my_source_file.copy( + &my_destination_file, + FileCopyFlags::NONE, + Cancellable::NONE, + Some(Box::new(move |current_num_bytes, total_num_bytes| { + assert!(current_num_bytes >= copied); + assert!(total_num_bytes >= total); + copied = current_num_bytes; + total = total_num_bytes; + })), + ); + assert!(expected.is_ok(), "{}", expected.unwrap_err()); + + // both results should equal + assert_eq!(res, expected); +} + +#[test] +fn file_move_() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::move_` + let my_custom_source_file = MyCustomFile::new("/my_file1"); + let my_custom_destination_file = MyCustomFile::new("/my_file2"); + let res = my_custom_source_file.move_( + &my_custom_destination_file, + FileCopyFlags::NONE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected move_"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::move_` + let my_source_file = MyFile::new("/my_file1"); + let my_destination_file = MyFile::new("/my_file2"); + let res = my_source_file.move_( + &my_destination_file, + FileCopyFlags::NONE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected move_"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + assert_eq!(err.kind::(), Some(IOErrorEnum::NotFound)); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::move_` + let my_custom_source_file = + MyCustomFile::with_type_state("/my_file1", FileType::Regular, MyFileState::Exist); + let my_custom_destination_file = + MyCustomFile::with_type_state("/my_file2", FileType::Regular, MyFileState::Exist); + let res = my_custom_source_file.move_( + &my_custom_destination_file, + FileCopyFlags::NONE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected move_"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::move_` + let my_source_file = + MyFile::with_type_state("/my_file1", FileType::Regular, MyFileState::Exist); + let my_destination_file = + MyFile::with_type_state("/my_file2", FileType::Regular, MyFileState::Exist); + let res = my_source_file.move_( + &my_destination_file, + FileCopyFlags::NONE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected move_"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + assert_eq!(err.kind::(), Some(IOErrorEnum::Exists)); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::move_` + let my_custom_source_file = + MyCustomFile::with_type_state("/my_file1", FileType::Regular, MyFileState::Exist); + let my_custom_destination_directory = + MyCustomFile::with_type_state("/my_dir2", FileType::Directory, MyFileState::Exist); + let res = my_custom_source_file.move_( + &my_custom_destination_directory, + FileCopyFlags::OVERWRITE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected move_"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::move_` + let my_source_file = + MyFile::with_type_state("/my_file1", FileType::Regular, MyFileState::Exist); + let my_destination_directory = + MyFile::with_type_state("/my_dir2", FileType::Directory, MyFileState::Exist); + let res = my_source_file.move_( + &my_destination_directory, + FileCopyFlags::OVERWRITE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected move_"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + assert_eq!(err.kind::(), Some(IOErrorEnum::IsDirectory)); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::move_` + let my_custom_source_directory = + MyCustomFile::with_type_state("/my_dir1", FileType::Directory, MyFileState::Exist); + let my_custom_destination_directory = + MyCustomFile::with_type_state("/my_dir2", FileType::Directory, MyFileState::Exist); + let res = my_custom_source_directory.move_( + &my_custom_destination_directory, + FileCopyFlags::OVERWRITE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected move_"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::move_` + let my_source_directory = + MyFile::with_type_state("/my_dir1", FileType::Directory, MyFileState::Exist); + let my_destination_directory = + MyFile::with_type_state("/my_dir2", FileType::Directory, MyFileState::Exist); + let res = my_source_directory.move_( + &my_destination_directory, + FileCopyFlags::OVERWRITE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected move_"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + assert_eq!(err.kind::(), Some(IOErrorEnum::WouldMerge)); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::move_` + let my_custom_source_directory = + MyCustomFile::with_type_state("/my_dir1", FileType::Directory, MyFileState::Exist); + let my_custom_destination_directory = + MyCustomFile::with_type_state("/my_dir2", FileType::Directory, MyFileState::DoesNotExist); + let res = my_custom_source_directory.move_( + &my_custom_destination_directory, + FileCopyFlags::NONE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected move_"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::move_` + let my_source_directory = + MyFile::with_type_state("/my_dir1", FileType::Directory, MyFileState::Exist); + let my_destination_directory = + MyFile::with_type_state("/my_dir2", FileType::Directory, MyFileState::DoesNotExist); + let res = my_source_directory.move_( + &my_destination_directory, + FileCopyFlags::NONE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected move_"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + assert_eq!(err.kind::(), Some(IOErrorEnum::WouldRecurse)); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::move_` + let my_custom_source_directory = + MyCustomFile::with_type_state("/my_dir1", FileType::Directory, MyFileState::Exist); + let my_custom_destination_file = + MyCustomFile::with_type_state("/my_file2", FileType::Regular, MyFileState::Exist); + let res = my_custom_source_directory.move_( + &my_custom_destination_file, + FileCopyFlags::OVERWRITE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected move_"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::move_` + let my_source_directory = + MyFile::with_type_state("/my_dir1", FileType::Directory, MyFileState::Exist); + let my_destination_file = + MyFile::with_type_state("/my_file2", FileType::Regular, MyFileState::Exist); + let res = my_source_directory.move_( + &my_destination_file, + FileCopyFlags::OVERWRITE, + Cancellable::NONE, + None, + ); + assert!(res.is_err(), "unexpected move_"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + assert_eq!(err.kind::(), Some(IOErrorEnum::WouldRecurse)); + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::move_` + let my_custom_source_file = + MyCustomFile::with_type_state("/my_file1", FileType::Regular, MyFileState::Exist); + let my_custom_destination_file = + MyCustomFile::with_type_state("/my_file2", FileType::Regular, MyFileState::DoesNotExist); + let (mut copied, mut total) = (0, 0); + let res = my_custom_source_file.move_( + &my_custom_destination_file, + FileCopyFlags::NONE, + Cancellable::NONE, + Some(Box::new(move |current_num_bytes, total_num_bytes| { + assert!(current_num_bytes >= copied); + assert!(total_num_bytes >= total); + copied = current_num_bytes; + total = total_num_bytes; + })), + ); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::move_` + let my_source_file = + MyFile::with_type_state("/my_file1", FileType::Regular, MyFileState::Exist); + let my_destination_file = + MyFile::with_type_state("/my_file2", FileType::Regular, MyFileState::DoesNotExist); + let (mut copied, mut total) = (0, 0); + let expected = my_source_file.move_( + &my_destination_file, + FileCopyFlags::NONE, + Cancellable::NONE, + Some(Box::new(move |current_num_bytes, total_num_bytes| { + assert!(current_num_bytes >= copied); + assert!(total_num_bytes >= total); + copied = current_num_bytes; + total = total_num_bytes; + })), + ); + assert!(expected.is_ok(), "{}", expected.unwrap_err()); + + // both results should equal + assert_eq!(res, expected); +} + +#[test] +fn file_mount_mountable() { + // run test in a main context dedicated and configured as the thread default one + let _ = glib::MainContext::new().with_thread_default(|| { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::mount_mountable` + let my_custom_path = MyCustomFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_custom_path.mount_mountable( + MountMountFlags::NONE, + MountOperation::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!(res.is_err(), "unexpected file mount mountable success"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::mount_mountable` + let my_path = MyFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_path.mount_mountable( + MountMountFlags::NONE, + MountOperation::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!(res.is_err(), "unexpected file mount mountable success"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + }); +} + +// checker-ignore-item +#[test] +fn file_unmount_mountable() { + // run test in a main context dedicated and configured as the thread default one + let _ = glib::MainContext::new().with_thread_default(|| { + // implement the deprecated function unmount_mountable which is useful for this test + fn unmount_mountable, P: FnOnce(Result<(), glib::Error>) + 'static>( + file: &T, + flags: MountUnmountFlags, + cancellable: Option<&impl IsA>, + callback: P, + ) { + use glib::translate::{from_glib_full, IntoGlib}; + use std::boxed::Box as Box_; + let main_context = glib::MainContext::ref_thread_default(); + let is_main_context_owner = main_context.is_owner(); + let has_acquired_main_context = (!is_main_context_owner) + .then(|| main_context.acquire().ok()) + .flatten(); + assert!( + is_main_context_owner || has_acquired_main_context.is_some(), + "Async operations only allowed if the thread is owning the MainContext" + ); + + let user_data: Box_> = + Box_::new(glib::thread_guard::ThreadGuard::new(callback)); + unsafe extern "C" fn unmount_mountable_trampoline< + P: FnOnce(Result<(), glib::Error>) + 'static, + >( + _source_object: *mut glib::gobject_ffi::GObject, + res: *mut crate::ffi::GAsyncResult, + user_data: glib::ffi::gpointer, + ) { + let mut error = std::ptr::null_mut(); + crate::ffi::g_file_unmount_mountable_finish( + _source_object as *mut _, + res, + &mut error, + ); + let result = if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + }; + let callback: Box_> = + Box_::from_raw(user_data as *mut _); + let callback: P = callback.into_inner(); + callback(result); + } + let callback = unmount_mountable_trampoline::

; + unsafe { + crate::ffi::g_file_unmount_mountable( + file.as_ref().to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + Some(callback), + Box_::into_raw(user_data) as *mut _, + ); + } + } + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::unmount_mountable` + let my_custom_path = MyCustomFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + unmount_mountable( + &my_custom_path, + MountUnmountFlags::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!(res.is_err(), "unexpected file unmount mountable success"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::unmount_mountable` + let my_path = MyFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + unmount_mountable( + &my_path, + MountUnmountFlags::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!(res.is_err(), "unexpected file unmount mountable success"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + }); +} + +// checker-ignore-item +#[test] +fn file_eject_mountable() { + // run test in a main context dedicated and configured as the thread default one + let _ = glib::MainContext::new().with_thread_default(|| { + // implement the deprecated function eject_mountable which is useful for this test + fn eject_mountable, P: FnOnce(Result<(), glib::Error>) + 'static>( + file: &T, + flags: MountUnmountFlags, + cancellable: Option<&impl IsA>, + callback: P, + ) { + use glib::translate::{from_glib_full, IntoGlib}; + use std::boxed::Box as Box_; + let main_context = glib::MainContext::ref_thread_default(); + let is_main_context_owner = main_context.is_owner(); + let has_acquired_main_context = (!is_main_context_owner) + .then(|| main_context.acquire().ok()) + .flatten(); + assert!( + is_main_context_owner || has_acquired_main_context.is_some(), + "Async operations only allowed if the thread is owning the MainContext" + ); + + let user_data: Box_> = + Box_::new(glib::thread_guard::ThreadGuard::new(callback)); + unsafe extern "C" fn eject_mountable_trampoline< + P: FnOnce(Result<(), glib::Error>) + 'static, + >( + _source_object: *mut glib::gobject_ffi::GObject, + res: *mut crate::ffi::GAsyncResult, + user_data: glib::ffi::gpointer, + ) { + let mut error = std::ptr::null_mut(); + crate::ffi::g_file_eject_mountable_finish( + _source_object as *mut _, + res, + &mut error, + ); + let result = if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + }; + let callback: Box_> = + Box_::from_raw(user_data as *mut _); + let callback: P = callback.into_inner(); + callback(result); + } + let callback = eject_mountable_trampoline::

; + unsafe { + crate::ffi::g_file_eject_mountable( + file.as_ref().to_glib_none().0, + flags.into_glib(), + cancellable.map(|p| p.as_ref()).to_glib_none().0, + Some(callback), + Box_::into_raw(user_data) as *mut _, + ); + } + } + + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::eject_mountable` + let my_custom_path = MyCustomFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + eject_mountable( + &my_custom_path, + MountUnmountFlags::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!(res.is_err(), "unexpected file eject mountable success"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::eject_mountable` + let my_path = MyFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + eject_mountable( + &my_path, + MountUnmountFlags::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!(res.is_err(), "unexpected file eject mountable success"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + }); +} + +#[test] +fn file_mount_enclosing_volume() { + // run test in a main context dedicated and configured as the thread default one + let _ = glib::MainContext::new().with_thread_default(|| { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::mount_enclosing_volume` + let my_custom_path = MyCustomFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_custom_path.mount_enclosing_volume( + MountMountFlags::NONE, + MountOperation::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!( + res.is_err(), + "unexpected file mount enclosing volume success" + ); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::mount_enclosing_volume` + let my_path = MyFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_path.mount_enclosing_volume( + MountMountFlags::NONE, + MountOperation::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!( + res.is_err(), + "unexpected file mount enclosing volume success" + ); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + }); +} + +#[test] +fn file_monitor_dir() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::monitor_dir` + let my_custom_file = + MyCustomFile::with_type_state("/my_file", FileType::Regular, MyFileState::Exist); + let res = my_custom_file.monitor_directory(FileMonitorFlags::NONE, Cancellable::NONE); + assert!(res.is_err(), "unexpected monitor"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::monitor_dir` + let my_file = MyFile::with_type_state("/my_file", FileType::Regular, MyFileState::Exist); + let res = my_file.monitor_directory(FileMonitorFlags::NONE, Cancellable::NONE); + assert!(res.is_err(), "unexpected monitor"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + + // run test in a main context dedicated and configured as the thread default one + let _ = glib::MainContext::new().with_thread_default(|| { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::monitor_dir` + let my_custom_dir = + MyCustomFile::with_type_state("/my_directory", FileType::Directory, MyFileState::Exist); + let res = my_custom_dir.monitor_directory(FileMonitorFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let my_custom_dir_monitor = res.unwrap(); + + let rx = { + let (tx, rx) = async_channel::bounded(3); + my_custom_dir_monitor.connect_changed(move |_, file, other_file, event_type| { + let res = glib::MainContext::ref_thread_default().block_on(tx.send(( + file.uri(), + other_file.map(File::uri), + event_type, + ))); + assert!(res.is_ok(), "{}", res.err().unwrap()); + }); + rx + }; + // emit 1st event + let my_custom_child = + MyCustomFile::with_type_state("/my_child", FileType::Regular, MyFileState::Exist); + my_custom_dir_monitor.emit_event( + &my_custom_child, + None::<&MyCustomFile>, + FileMonitorEvent::Created, + ); + // emit 2nd event + my_custom_dir_monitor.emit_event( + &my_custom_child, + None::<&MyCustomFile>, + FileMonitorEvent::Changed, + ); + // emit 3rd event + my_custom_dir_monitor.emit_event( + &my_custom_child, + None::<&MyCustomFile>, + FileMonitorEvent::ChangesDoneHint, + ); + let res = glib::MainContext::ref_thread_default().block_on(rx.recv()); + assert!(res.is_ok(), "{}", res.err().unwrap()); + let event1 = res.unwrap(); + let res = glib::MainContext::ref_thread_default().block_on(rx.recv()); + assert!(res.is_ok(), "{}", res.err().unwrap()); + let event2 = res.unwrap(); + let res = glib::MainContext::ref_thread_default().block_on(rx.recv()); + assert!(res.is_ok(), "{}", res.err().unwrap()); + let event3 = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::monitor_dir` + let my_dir = + MyFile::with_type_state("/my_directory", FileType::Directory, MyFileState::Exist); + let res = my_dir.monitor_directory(FileMonitorFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let my_dir_monitor = res.unwrap(); + + let rx = { + let (tx, rx) = async_channel::bounded(3); + my_dir_monitor.connect_changed(move |_, file, other_file, event_type| { + let res = glib::MainContext::ref_thread_default().block_on(tx.send(( + file.uri(), + other_file.map(File::uri), + event_type, + ))); + assert!(res.is_ok(), "{}", res.err().unwrap()); + }); + rx + }; + // emit an event + let my_child = MyFile::with_type_state("/my_child", FileType::Regular, MyFileState::Exist); + my_dir_monitor.emit_event(&my_child, None::<&MyFile>, FileMonitorEvent::Created); + my_dir_monitor.emit_event(&my_child, None::<&MyFile>, FileMonitorEvent::Changed); + my_dir_monitor.emit_event( + &my_child, + None::<&MyFile>, + FileMonitorEvent::ChangesDoneHint, + ); + let res = glib::MainContext::ref_thread_default().block_on(rx.recv()); + assert!(res.is_ok(), "{}", res.err().unwrap()); + let expected1 = res.unwrap(); + let res = glib::MainContext::ref_thread_default().block_on(rx.recv()); + assert!(res.is_ok(), "{}", res.err().unwrap()); + let expected2 = res.unwrap(); + let res = glib::MainContext::ref_thread_default().block_on(rx.recv()); + assert!(res.is_ok(), "{}", res.err().unwrap()); + let expected3 = res.unwrap(); + + // both events should equal + assert_eq!(event1, expected1); + assert_eq!(event2, expected2); + assert_eq!(event3, expected3); + }); +} + +#[test] +fn file_monitor_file() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::monitor_file` + let my_custom_dir = + MyCustomFile::with_type_state("/my_directory", FileType::Directory, MyFileState::Exist); + let res = my_custom_dir.monitor_file(FileMonitorFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let monitor = res.unwrap(); + assert_ne!(monitor.type_(), MyFileMonitor::static_type()); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::monitor_file` + let my_dir = MyFile::with_type_state("/my_directory", FileType::Directory, MyFileState::Exist); + let res = my_dir.monitor_file(FileMonitorFlags::NONE, Cancellable::NONE); + let expected = res.unwrap(); + assert_ne!(expected.type_(), MyFileMonitor::static_type()); + + // both results should equal + assert_eq!(monitor.type_(), expected.type_()); + + // run test in a main context dedicated and configured as the thread default one + let _ = glib::MainContext::new().with_thread_default(|| { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::monitor_file` + let my_custom_file = + MyCustomFile::with_type_state("/my_file", FileType::Regular, MyFileState::Exist); + let res = my_custom_file.monitor_file(FileMonitorFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let my_custom_file_monitor = res.unwrap(); + + let rx = { + let (tx, rx) = async_channel::bounded(2); + my_custom_file_monitor.connect_changed(move |_, file, other_file, event_type| { + let res = glib::MainContext::ref_thread_default().block_on(tx.send(( + file.uri(), + other_file.map(File::uri), + event_type, + ))); + assert!(res.is_ok(), "{}", res.err().unwrap()); + }); + rx + }; + // emit 1st event + my_custom_file_monitor.emit_event( + &my_custom_file, + None::<&MyCustomFile>, + FileMonitorEvent::Changed, + ); + // emit 2nd event + my_custom_file_monitor.emit_event( + &my_custom_file, + None::<&MyCustomFile>, + FileMonitorEvent::ChangesDoneHint, + ); + let res = glib::MainContext::ref_thread_default().block_on(rx.recv()); + assert!(res.is_ok(), "{}", res.err().unwrap()); + let event1 = res.unwrap(); + let res = glib::MainContext::ref_thread_default().block_on(rx.recv()); + assert!(res.is_ok(), "{}", res.err().unwrap()); + let event2 = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::monitor_file` + let my_file = MyFile::with_type_state("/my_file", FileType::Regular, MyFileState::Exist); + let res = my_file.monitor_file(FileMonitorFlags::NONE, Cancellable::NONE); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let my_file_monitor = res.unwrap(); + + let rx = { + let (tx, rx) = async_channel::bounded(2); + my_file_monitor.connect_changed(move |_, file, other_file, event_type| { + let res = glib::MainContext::ref_thread_default().block_on(tx.send(( + file.uri(), + other_file.map(File::uri), + event_type, + ))); + assert!(res.is_ok(), "{}", res.err().unwrap()); + }); + rx + }; + // emit an event + my_file_monitor.emit_event(&my_file, None::<&MyFile>, FileMonitorEvent::Changed); + my_file_monitor.emit_event(&my_file, None::<&MyFile>, FileMonitorEvent::ChangesDoneHint); + let res = glib::MainContext::ref_thread_default().block_on(rx.recv()); + assert!(res.is_ok(), "{}", res.err().unwrap()); + let expected1 = res.unwrap(); + let res = glib::MainContext::ref_thread_default().block_on(rx.recv()); + assert!(res.is_ok(), "{}", res.err().unwrap()); + let expected2 = res.unwrap(); + + // both events should equal + assert_eq!(event1, expected1); + assert_eq!(event2, expected2); + }); +} + +#[test] +fn file_open_readwrite() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::open_readwrite` + let my_custom_file = MyCustomFile::new("/my_file"); + let res = my_custom_file.open_readwrite(Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file open read write success {:?}", + res.ok().unwrap() + ); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::open_readwrite` + let my_file = MyFile::new("/my_file"); + let res = my_file.open_readwrite(Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file open read write success {:?}", + res.ok().unwrap() + ); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); +} + +#[test] +fn file_create_readwrite() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::create_readwrite` + let my_custom_file = MyCustomFile::new("/my_file"); + let res = my_custom_file.create_readwrite(FileCreateFlags::NONE, Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file create read write success {:?}", + res.ok().unwrap() + ); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::create_readwrite` + let my_file = MyFile::new("/my_file"); + let res = my_file.create_readwrite(FileCreateFlags::NONE, Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file create read write success {:?}", + res.ok().unwrap() + ); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); +} + +#[test] +fn file_replace_readwrite() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::replace_readwrite` + let my_custom_file = MyCustomFile::new("/my_file"); + let res = + my_custom_file.replace_readwrite(None, false, FileCreateFlags::NONE, Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file replace read write success {:?}", + res.ok().unwrap() + ); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::replace_readwrite` + let my_file = MyFile::new("/my_file"); + let res = my_file.replace_readwrite(None, false, FileCreateFlags::NONE, Cancellable::NONE); + assert!( + res.is_err(), + "unexpected file replace read write success {:?}", + res.ok().unwrap() + ); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); +} + +#[test] +fn file_start_mountable() { + // run test in a main context dedicated and configured as the thread default one + let _ = glib::MainContext::new().with_thread_default(|| { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::start_mountable` + let my_custom_path = MyCustomFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_custom_path.start_mountable( + DriveStartFlags::NONE, + MountOperation::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!(res.is_err(), "unexpected file start mountable success"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::start_mountable` + let my_path = MyFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_path.start_mountable( + DriveStartFlags::NONE, + MountOperation::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!(res.is_err(), "unexpected file start mountable success"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + }); +} + +#[test] +fn file_stop_mountable() { + // run test in a main context dedicated and configured as the thread default one + let _ = glib::MainContext::new().with_thread_default(|| { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::stop_mountable` + let my_custom_path = MyCustomFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_custom_path.stop_mountable( + MountUnmountFlags::NONE, + MountOperation::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!(res.is_err(), "unexpected file stop mountable success"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::stop_mountable` + let my_path = MyFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_path.stop_mountable( + MountUnmountFlags::NONE, + MountOperation::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!(res.is_err(), "unexpected file stop mountable success"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + }); +} + +#[test] +fn file_unmount_mountable_with_operation() { + // run test in a main context dedicated and configured as the thread default one + let _ = glib::MainContext::new().with_thread_default(|| { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::unmount_mountable_with_operation` + let my_custom_path = MyCustomFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_custom_path.unmount_mountable_with_operation( + MountUnmountFlags::NONE, + MountOperation::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!( + res.is_err(), + "unexpected file unmount mountable with operation success" + ); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::unmount_mountable_with_operation` + let my_path = MyFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_path.unmount_mountable_with_operation( + MountUnmountFlags::NONE, + MountOperation::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!( + res.is_err(), + "unexpected file unmount mountable with operation success" + ); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + }); +} + +#[test] +fn file_eject_mountable_with_operation() { + // run test in a main context dedicated and configured as the thread default one + let _ = glib::MainContext::new().with_thread_default(|| { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::eject_mountable_with_operation` + let my_custom_path = MyCustomFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_custom_path.eject_mountable_with_operation( + MountUnmountFlags::NONE, + MountOperation::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!( + res.is_err(), + "unexpected file eject mountable with operation success" + ); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::eject_mountable_with_operation` + let my_path = MyFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_path.eject_mountable_with_operation( + MountUnmountFlags::NONE, + MountOperation::NONE, + Cancellable::NONE, + move |res| tx.send(res).unwrap(), + ); + rx.await.unwrap() + }); + assert!( + res.is_err(), + "unexpected file eject mountable with operation success" + ); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + }); +} + +#[test] +fn file_poll_mountable() { + // run test in a main context dedicated and configured as the thread default one + let _ = glib::MainContext::new().with_thread_default(|| { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::poll_mountable` + let my_custom_path = MyCustomFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_custom_path.poll_mountable(Cancellable::NONE, move |res| tx.send(res).unwrap()); + rx.await.unwrap() + }); + assert!(res.is_err(), "unexpected file poll mountable success"); + let err = res.unwrap_err(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::poll_mountable` + let my_path = MyFile::new("/my_path"); + + let (tx, rx) = oneshot::channel(); + let res = glib::MainContext::ref_thread_default().block_on(async { + my_path.poll_mountable(Cancellable::NONE, move |res| tx.send(res).unwrap()); + rx.await.unwrap() + }); + assert!(res.is_err(), "unexpected file poll mountable success"); + let expected = res.unwrap_err(); + + // both errors should equal + assert_eq!(err.message(), expected.message()); + assert_eq!(err.kind::(), expected.kind::()); + }); +} + +#[test] +fn file_measure_disk_usage() { + // invoke `MyCustomFile` implementation of `crate::ffi::GFileIface::measure_disk_usage` + let my_custom_file = + MyCustomFile::with_type_state("/my_file", FileType::Regular, MyFileState::Exist); + + let (mut completed, mut size, mut nb_dirs, mut nb_files) = (false, 0u64, 0u64, 0u64); + let res = my_custom_file.measure_disk_usage( + FileMeasureFlags::NONE, + Cancellable::NONE, + Some(Box::new( + move |reporting: bool, current_size, num_dirs, num_files| { + if reporting { + assert!(!completed); + } + assert!(current_size >= size); + assert!(num_dirs >= nb_dirs); + assert!(num_files >= nb_files); + completed = !reporting; + size = current_size; + nb_dirs = num_dirs; + nb_files = num_files; + }, + )), + ); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let result = res.unwrap(); + + // invoke `MyFile` implementation of `crate::ffi::GFileIface::measure_disk_usage` + let my_file = MyFile::with_type_state("/my_file", FileType::Regular, MyFileState::Exist); + let (mut completed, mut size, mut nb_dirs, mut nb_files) = (false, 0u64, 0u64, 0u64); + let res = my_file.measure_disk_usage( + FileMeasureFlags::NONE, + Cancellable::NONE, + Some(Box::new( + move |reporting: bool, current_size, num_dirs, num_files| { + if reporting { + assert!(!completed); + } + assert!(current_size >= size); + assert!(num_dirs >= nb_dirs); + assert!(num_files >= nb_files); + completed = !reporting; + size = current_size; + nb_dirs = num_dirs; + nb_files = num_files; + }, + )), + ); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let expected = res.unwrap(); + + // both results should equal + assert_eq!(result, expected); +} diff --git a/gio/src/subclass/mod.rs b/gio/src/subclass/mod.rs index e2626fa35c65..a99a96f21c54 100644 --- a/gio/src/subclass/mod.rs +++ b/gio/src/subclass/mod.rs @@ -4,6 +4,7 @@ mod action_group; mod action_map; mod application; mod async_initable; +mod file; mod file_enumerator; mod file_monitor; mod initable; @@ -26,6 +27,7 @@ pub mod prelude { action_map::{ActionMapImpl, ActionMapImplExt}, application::{ApplicationImpl, ApplicationImplExt}, async_initable::{AsyncInitableImpl, AsyncInitableImplExt}, + file::{FileImpl, FileImplExt}, file_enumerator::{FileEnumeratorImpl, FileEnumeratorImplExt}, file_monitor::{FileMonitorImpl, FileMonitorImplExt}, initable::{InitableImpl, InitableImplExt},